1 /*
2 * ra_plugin.c : the main RA module for local repository access
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 #include "ra_local.h"
25 #include "svn_hash.h"
26 #include "svn_ra.h"
27 #include "svn_fs.h"
28 #include "svn_delta.h"
29 #include "svn_repos.h"
30 #include "svn_pools.h"
31 #include "svn_time.h"
32 #include "svn_props.h"
33 #include "svn_mergeinfo.h"
34 #include "svn_path.h"
35 #include "svn_version.h"
36 #include "svn_cache_config.h"
37
38 #include "svn_private_config.h"
39 #include "../libsvn_ra/ra_loader.h"
40 #include "private/svn_mergeinfo_private.h"
41 #include "private/svn_repos_private.h"
42 #include "private/svn_fspath.h"
43 #include "private/svn_atomic.h"
44 #include "private/svn_subr_private.h"
45
46 #define APR_WANT_STRFUNC
47 #include <apr_want.h>
48
49 /*----------------------------------------------------------------*/
50
51 /*** Miscellaneous helper functions ***/
52
53
54 /* Pool cleanup handler: ensure that the access descriptor of the
55 filesystem (svn_fs_t *) DATA is set to NULL. */
56 static apr_status_t
cleanup_access(void * data)57 cleanup_access(void *data)
58 {
59 svn_error_t *serr;
60 svn_fs_t *fs = data;
61
62 serr = svn_fs_set_access(fs, NULL);
63
64 if (serr)
65 {
66 apr_status_t apr_err = serr->apr_err;
67 svn_error_clear(serr);
68 return apr_err;
69 }
70
71 return APR_SUCCESS;
72 }
73
74
75 /* Fetch a username for use with SESSION, and store it in SESSION->username.
76 *
77 * Allocate the username in SESSION->pool. Use SCRATCH_POOL for temporary
78 * allocations. */
79 static svn_error_t *
get_username(svn_ra_session_t * session,apr_pool_t * scratch_pool)80 get_username(svn_ra_session_t *session,
81 apr_pool_t *scratch_pool)
82 {
83 svn_ra_local__session_baton_t *sess = session->priv;
84
85 /* If we've already found the username don't ask for it again. */
86 if (! sess->username)
87 {
88 /* Get a username somehow, so we have some svn:author property to
89 attach to a commit. */
90 if (sess->auth_baton)
91 {
92 void *creds;
93 svn_auth_cred_username_t *username_creds;
94 svn_auth_iterstate_t *iterstate;
95
96 SVN_ERR(svn_auth_first_credentials(&creds, &iterstate,
97 SVN_AUTH_CRED_USERNAME,
98 sess->uuid, /* realmstring */
99 sess->auth_baton,
100 scratch_pool));
101
102 /* No point in calling next_creds(), since that assumes that the
103 first_creds() somehow failed to authenticate. But there's no
104 challenge going on, so we use whatever creds we get back on
105 the first try. */
106 username_creds = creds;
107 if (username_creds && username_creds->username)
108 {
109 sess->username = apr_pstrdup(session->pool,
110 username_creds->username);
111 svn_error_clear(svn_auth_save_credentials(iterstate,
112 scratch_pool));
113 }
114 else
115 sess->username = "";
116 }
117 else
118 sess->username = "";
119 }
120
121 /* If we have a real username, attach it to the filesystem so that it can
122 be used to validate locks. Even if there already is a user context
123 associated, it may contain irrelevant lock tokens, so always create a new.
124 */
125 if (*sess->username)
126 {
127 svn_fs_access_t *access_ctx;
128
129 SVN_ERR(svn_fs_create_access(&access_ctx, sess->username,
130 session->pool));
131 SVN_ERR(svn_fs_set_access(sess->fs, access_ctx));
132
133 /* Make sure this context is disassociated when the pool gets
134 destroyed. */
135 apr_pool_cleanup_register(session->pool, sess->fs, cleanup_access,
136 apr_pool_cleanup_null);
137 }
138
139 return SVN_NO_ERROR;
140 }
141
142 /* Implements an svn_atomic__init_once callback. Sets the FSFS memory
143 cache size. */
144 static svn_error_t *
cache_init(void * baton,apr_pool_t * pool)145 cache_init(void *baton, apr_pool_t *pool)
146 {
147 apr_hash_t *config_hash = baton;
148 svn_config_t *config = NULL;
149 const char *memory_cache_size_str;
150
151 if (config_hash)
152 config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG);
153 svn_config_get(config, &memory_cache_size_str, SVN_CONFIG_SECTION_MISCELLANY,
154 SVN_CONFIG_OPTION_MEMORY_CACHE_SIZE, NULL);
155 if (memory_cache_size_str)
156 {
157 apr_uint64_t memory_cache_size;
158 svn_cache_config_t settings = *svn_cache_config_get();
159
160 SVN_ERR(svn_error_quick_wrap(svn_cstring_atoui64(&memory_cache_size,
161 memory_cache_size_str),
162 _("memory-cache-size invalid")));
163 settings.cache_size = 1024 * 1024 * memory_cache_size;
164 svn_cache_config_set(&settings);
165 }
166
167 return SVN_NO_ERROR;
168 }
169
170 /*----------------------------------------------------------------*/
171
172 /*** The reporter vtable needed by do_update() and friends ***/
173
174 typedef struct reporter_baton_t
175 {
176 svn_ra_local__session_baton_t *sess;
177 void *report_baton;
178
179 } reporter_baton_t;
180
181
182 static void *
make_reporter_baton(svn_ra_local__session_baton_t * sess,void * report_baton,apr_pool_t * pool)183 make_reporter_baton(svn_ra_local__session_baton_t *sess,
184 void *report_baton,
185 apr_pool_t *pool)
186 {
187 reporter_baton_t *rbaton = apr_palloc(pool, sizeof(*rbaton));
188 rbaton->sess = sess;
189 rbaton->report_baton = report_baton;
190 return rbaton;
191 }
192
193
194 static svn_error_t *
reporter_set_path(void * reporter_baton,const char * path,svn_revnum_t revision,svn_depth_t depth,svn_boolean_t start_empty,const char * lock_token,apr_pool_t * pool)195 reporter_set_path(void *reporter_baton,
196 const char *path,
197 svn_revnum_t revision,
198 svn_depth_t depth,
199 svn_boolean_t start_empty,
200 const char *lock_token,
201 apr_pool_t *pool)
202 {
203 reporter_baton_t *rbaton = reporter_baton;
204 return svn_repos_set_path3(rbaton->report_baton, path,
205 revision, depth, start_empty, lock_token, pool);
206 }
207
208
209 static svn_error_t *
reporter_delete_path(void * reporter_baton,const char * path,apr_pool_t * pool)210 reporter_delete_path(void *reporter_baton,
211 const char *path,
212 apr_pool_t *pool)
213 {
214 reporter_baton_t *rbaton = reporter_baton;
215 return svn_repos_delete_path(rbaton->report_baton, path, pool);
216 }
217
218
219 static svn_error_t *
reporter_link_path(void * reporter_baton,const char * path,const char * url,svn_revnum_t revision,svn_depth_t depth,svn_boolean_t start_empty,const char * lock_token,apr_pool_t * pool)220 reporter_link_path(void *reporter_baton,
221 const char *path,
222 const char *url,
223 svn_revnum_t revision,
224 svn_depth_t depth,
225 svn_boolean_t start_empty,
226 const char *lock_token,
227 apr_pool_t *pool)
228 {
229 reporter_baton_t *rbaton = reporter_baton;
230 const char *repos_url = rbaton->sess->repos_url;
231 const char *relpath = svn_uri_skip_ancestor(repos_url, url, pool);
232 const char *fs_path;
233
234 if (!relpath)
235 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
236 _("'%s'\n"
237 "is not the same repository as\n"
238 "'%s'"), url, rbaton->sess->repos_url);
239
240 /* Convert the relpath to an fspath */
241 if (relpath[0] == '\0')
242 fs_path = "/";
243 else
244 fs_path = apr_pstrcat(pool, "/", relpath, SVN_VA_NULL);
245
246 return svn_repos_link_path3(rbaton->report_baton, path, fs_path, revision,
247 depth, start_empty, lock_token, pool);
248 }
249
250
251 static svn_error_t *
reporter_finish_report(void * reporter_baton,apr_pool_t * pool)252 reporter_finish_report(void *reporter_baton,
253 apr_pool_t *pool)
254 {
255 reporter_baton_t *rbaton = reporter_baton;
256 return svn_repos_finish_report(rbaton->report_baton, pool);
257 }
258
259
260 static svn_error_t *
reporter_abort_report(void * reporter_baton,apr_pool_t * pool)261 reporter_abort_report(void *reporter_baton,
262 apr_pool_t *pool)
263 {
264 reporter_baton_t *rbaton = reporter_baton;
265 return svn_repos_abort_report(rbaton->report_baton, pool);
266 }
267
268
269 static const svn_ra_reporter3_t ra_local_reporter =
270 {
271 reporter_set_path,
272 reporter_delete_path,
273 reporter_link_path,
274 reporter_finish_report,
275 reporter_abort_report
276 };
277
278
279 /* ...
280 *
281 * Wrap a cancellation editor using SESSION's cancellation function around
282 * the supplied EDITOR. ### Some callers (via svn_ra_do_update2() etc.)
283 * don't appear to know that we do this, and are supplying an editor that
284 * they have already wrapped with the same cancellation editor, so it ends
285 * up double-wrapped.
286 *
287 * Allocate @a *reporter and @a *report_baton in @a result_pool. Use
288 * @a scratch_pool for temporary allocations.
289 */
290 static svn_error_t *
make_reporter(svn_ra_session_t * session,const svn_ra_reporter3_t ** reporter,void ** report_baton,svn_revnum_t revision,const char * target,const char * other_url,svn_boolean_t text_deltas,svn_depth_t depth,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry,const svn_delta_editor_t * editor,void * edit_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)291 make_reporter(svn_ra_session_t *session,
292 const svn_ra_reporter3_t **reporter,
293 void **report_baton,
294 svn_revnum_t revision,
295 const char *target,
296 const char *other_url,
297 svn_boolean_t text_deltas,
298 svn_depth_t depth,
299 svn_boolean_t send_copyfrom_args,
300 svn_boolean_t ignore_ancestry,
301 const svn_delta_editor_t *editor,
302 void *edit_baton,
303 apr_pool_t *result_pool,
304 apr_pool_t *scratch_pool)
305 {
306 svn_ra_local__session_baton_t *sess = session->priv;
307 void *rbaton;
308 const char *other_fs_path = NULL;
309
310 /* Get the HEAD revision if one is not supplied. */
311 if (! SVN_IS_VALID_REVNUM(revision))
312 SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, scratch_pool));
313
314 /* If OTHER_URL was provided, validate it and convert it into a
315 regular filesystem path. */
316 if (other_url)
317 {
318 const char *other_relpath
319 = svn_uri_skip_ancestor(sess->repos_url, other_url, scratch_pool);
320
321 /* Sanity check: the other_url better be in the same repository as
322 the original session url! */
323 if (! other_relpath)
324 return svn_error_createf
325 (SVN_ERR_RA_ILLEGAL_URL, NULL,
326 _("'%s'\n"
327 "is not the same repository as\n"
328 "'%s'"), other_url, sess->repos_url);
329
330 other_fs_path = apr_pstrcat(scratch_pool, "/", other_relpath,
331 SVN_VA_NULL);
332 }
333
334 /* Pass back our reporter */
335 *reporter = &ra_local_reporter;
336
337 SVN_ERR(get_username(session, scratch_pool));
338
339 if (sess->callbacks)
340 SVN_ERR(svn_delta_get_cancellation_editor(sess->callbacks->cancel_func,
341 sess->callback_baton,
342 editor,
343 edit_baton,
344 &editor,
345 &edit_baton,
346 result_pool));
347
348 /* Build a reporter baton. */
349 SVN_ERR(svn_repos_begin_report3(&rbaton,
350 revision,
351 sess->repos,
352 sess->fs_path->data,
353 target,
354 other_fs_path,
355 text_deltas,
356 depth,
357 ignore_ancestry,
358 send_copyfrom_args,
359 editor,
360 edit_baton,
361 NULL,
362 NULL,
363 0, /* Disable zero-copy codepath, because
364 RA API users are unaware about the
365 zero-copy code path limitation (do
366 not access FSFS data structures
367 and, hence, caches). See notes
368 to svn_repos_begin_report3() for
369 additional details. */
370 result_pool));
371
372 /* Wrap the report baton given us by the repos layer with our own
373 reporter baton. */
374 *report_baton = make_reporter_baton(sess, rbaton, result_pool);
375
376 return SVN_NO_ERROR;
377 }
378
379
380 /*----------------------------------------------------------------*/
381
382 /*** Deltification stuff for get_commit_editor() ***/
383
384 struct deltify_etc_baton
385 {
386 svn_fs_t *fs; /* the fs to deltify in */
387 svn_repos_t *repos; /* repos for unlocking */
388 const char *fspath_base; /* fs-path part of split session URL */
389
390 apr_hash_t *lock_tokens; /* tokens to unlock, if any */
391
392 svn_commit_callback2_t commit_cb; /* the original callback */
393 void *commit_baton; /* the original callback's baton */
394 };
395
396 /* This implements 'svn_commit_callback_t'. Its invokes the original
397 (wrapped) callback, but also does deltification on the new revision and
398 possibly unlocks committed paths.
399 BATON is 'struct deltify_etc_baton *'. */
400 static svn_error_t *
deltify_etc(const svn_commit_info_t * commit_info,void * baton,apr_pool_t * scratch_pool)401 deltify_etc(const svn_commit_info_t *commit_info,
402 void *baton,
403 apr_pool_t *scratch_pool)
404 {
405 struct deltify_etc_baton *deb = baton;
406 svn_error_t *err1 = SVN_NO_ERROR;
407 svn_error_t *err2;
408
409 /* Invoke the original callback first, in case someone's waiting to
410 know the revision number so they can go off and annotate an
411 issue or something. */
412 if (deb->commit_cb)
413 err1 = deb->commit_cb(commit_info, deb->commit_baton, scratch_pool);
414
415 /* Maybe unlock the paths. */
416 if (deb->lock_tokens)
417 {
418 apr_pool_t *subpool = svn_pool_create(scratch_pool);
419 apr_hash_t *targets = apr_hash_make(subpool);
420 apr_hash_index_t *hi;
421
422 for (hi = apr_hash_first(subpool, deb->lock_tokens); hi;
423 hi = apr_hash_next(hi))
424 {
425 const void *relpath = apr_hash_this_key(hi);
426 const char *token = apr_hash_this_val(hi);
427 const char *fspath;
428
429 fspath = svn_fspath__join(deb->fspath_base, relpath, subpool);
430 svn_hash_sets(targets, fspath, token);
431 }
432
433 /* We may get errors here if the lock was broken or stolen
434 after the commit succeeded. This is fine and should be
435 ignored. */
436 svn_error_clear(svn_repos_fs_unlock_many(deb->repos, targets, FALSE,
437 NULL, NULL,
438 subpool, subpool));
439
440 svn_pool_destroy(subpool);
441 }
442
443 /* But, deltification shouldn't be stopped just because someone's
444 random callback failed, so proceed unconditionally on to
445 deltification. */
446 err2 = svn_fs_deltify_revision(deb->fs, commit_info->revision, scratch_pool);
447
448 return svn_error_compose_create(err1, err2);
449 }
450
451
452 /* If LOCK_TOKENS is not NULL, then copy all tokens into the access context
453 of FS. The tokens' paths will be prepended with FSPATH_BASE.
454
455 ACCESS_POOL must match (or exceed) the lifetime of the access context
456 that was associated with FS. Typically, this is the session pool.
457
458 Temporary allocations are made in SCRATCH_POOL. */
459 static svn_error_t *
apply_lock_tokens(svn_fs_t * fs,const char * fspath_base,apr_hash_t * lock_tokens,apr_pool_t * access_pool,apr_pool_t * scratch_pool)460 apply_lock_tokens(svn_fs_t *fs,
461 const char *fspath_base,
462 apr_hash_t *lock_tokens,
463 apr_pool_t *access_pool,
464 apr_pool_t *scratch_pool)
465 {
466 if (lock_tokens)
467 {
468 svn_fs_access_t *access_ctx;
469
470 SVN_ERR(svn_fs_get_access(&access_ctx, fs));
471
472 /* If there is no access context, the filesystem will scream if a
473 lock is needed. */
474 if (access_ctx)
475 {
476 apr_hash_index_t *hi;
477
478 /* Note: we have no use for an iterpool here since the data
479 within the loop is copied into ACCESS_POOL. */
480
481 for (hi = apr_hash_first(scratch_pool, lock_tokens); hi;
482 hi = apr_hash_next(hi))
483 {
484 const void *relpath = apr_hash_this_key(hi);
485 const char *token = apr_hash_this_val(hi);
486 const char *fspath;
487
488 /* The path needs to live as long as ACCESS_CTX. */
489 fspath = svn_fspath__join(fspath_base, relpath, access_pool);
490
491 /* The token must live as long as ACCESS_CTX. */
492 token = apr_pstrdup(access_pool, token);
493
494 SVN_ERR(svn_fs_access_add_lock_token2(access_ctx, fspath,
495 token));
496 }
497 }
498 }
499
500 return SVN_NO_ERROR;
501 }
502
503
504 /*----------------------------------------------------------------*/
505
506 /*** The RA vtable routines ***/
507
508 #define RA_LOCAL_DESCRIPTION \
509 N_("Module for accessing a repository on local disk.")
510
511 static const char *
svn_ra_local__get_description(apr_pool_t * pool)512 svn_ra_local__get_description(apr_pool_t *pool)
513 {
514 return _(RA_LOCAL_DESCRIPTION);
515 }
516
517 static const char * const *
svn_ra_local__get_schemes(apr_pool_t * pool)518 svn_ra_local__get_schemes(apr_pool_t *pool)
519 {
520 static const char *schemes[] = { "file", NULL };
521
522 return schemes;
523 }
524
525 /* Do nothing.
526 *
527 * Why is this acceptable? FS warnings used to be used for only
528 * two things: failures to close BDB repositories and failures to
529 * interact with memcached in FSFS (new in 1.6). In 1.5 and earlier,
530 * we did not call svn_fs_set_warning_func in ra_local, which means
531 * that any BDB-closing failure would have led to abort()s; the fact
532 * that this hasn't led to huge hues and cries makes it seem likely
533 * that this just doesn't happen that often, at least not through
534 * ra_local. And as far as memcached goes, it seems unlikely that
535 * somebody is going to go through the trouble of setting up and
536 * running memcached servers but then use ra_local access. So we
537 * ignore errors here, so that memcached can use the FS warnings API
538 * without crashing ra_local.
539 */
540 static void
ignore_warnings(void * baton,svn_error_t * err)541 ignore_warnings(void *baton,
542 svn_error_t *err)
543 {
544 #ifdef SVN_DEBUG
545 SVN_DBG(("Ignoring FS warning %s\n",
546 svn_error_symbolic_name(err ? err->apr_err : 0)));
547 #endif
548 return;
549 }
550
551 #define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \
552 " ra_local"
553
554 static svn_error_t *
svn_ra_local__open(svn_ra_session_t * session,const char ** corrected_url,const char ** redirect_url,const char * repos_URL,const svn_ra_callbacks2_t * callbacks,void * callback_baton,svn_auth_baton_t * auth_baton,apr_hash_t * config,apr_pool_t * result_pool,apr_pool_t * scratch_pool)555 svn_ra_local__open(svn_ra_session_t *session,
556 const char **corrected_url,
557 const char **redirect_url,
558 const char *repos_URL,
559 const svn_ra_callbacks2_t *callbacks,
560 void *callback_baton,
561 svn_auth_baton_t *auth_baton,
562 apr_hash_t *config,
563 apr_pool_t *result_pool,
564 apr_pool_t *scratch_pool)
565 {
566 const char *client_string;
567 svn_ra_local__session_baton_t *sess;
568 const char *fs_path;
569 static volatile svn_atomic_t cache_init_state = 0;
570 apr_pool_t *pool = result_pool;
571
572 /* Initialise the FSFS memory cache size. We can only do this once
573 so one CONFIG will win the race and all others will be ignored
574 silently. */
575 SVN_ERR(svn_atomic__init_once(&cache_init_state, cache_init, config, pool));
576
577 /* We don't support redirections in ra-local. */
578 if (corrected_url)
579 *corrected_url = NULL;
580 if (redirect_url)
581 *redirect_url = NULL;
582
583 /* Allocate and stash the session_sess args we have already. */
584 sess = apr_pcalloc(pool, sizeof(*sess));
585 sess->callbacks = callbacks;
586 sess->callback_baton = callback_baton;
587 sess->auth_baton = auth_baton;
588
589 /* Look through the URL, figure out which part points to the
590 repository, and which part is the path *within* the
591 repository. */
592 SVN_ERR(svn_ra_local__split_URL(&(sess->repos),
593 &(sess->repos_url),
594 &fs_path,
595 repos_URL,
596 session->pool));
597 sess->fs_path = svn_stringbuf_create(fs_path, session->pool);
598
599 /* Cache the filesystem object from the repos here for
600 convenience. */
601 sess->fs = svn_repos_fs(sess->repos);
602
603 /* Ignore FS warnings. */
604 svn_fs_set_warning_func(sess->fs, ignore_warnings, NULL);
605
606 /* Cache the repository UUID as well */
607 SVN_ERR(svn_fs_get_uuid(sess->fs, &sess->uuid, session->pool));
608
609 /* Be sure username is NULL so we know to look it up / ask for it */
610 sess->username = NULL;
611
612 if (sess->callbacks->get_client_string != NULL)
613 SVN_ERR(sess->callbacks->get_client_string(sess->callback_baton,
614 &client_string, pool));
615 else
616 client_string = NULL;
617
618 if (client_string)
619 sess->useragent = apr_pstrcat(pool, USER_AGENT " ",
620 client_string, SVN_VA_NULL);
621 else
622 sess->useragent = USER_AGENT;
623
624 session->priv = sess;
625 return SVN_NO_ERROR;
626 }
627
628 static svn_error_t *
svn_ra_local__dup_session(svn_ra_session_t * new_session,svn_ra_session_t * session,const char * new_session_url,apr_pool_t * result_pool,apr_pool_t * scratch_pool)629 svn_ra_local__dup_session(svn_ra_session_t *new_session,
630 svn_ra_session_t *session,
631 const char *new_session_url,
632 apr_pool_t *result_pool,
633 apr_pool_t *scratch_pool)
634 {
635 svn_ra_local__session_baton_t *old_sess = session->priv;
636 svn_ra_local__session_baton_t *new_sess;
637 const char *fs_path;
638
639 /* Allocate and stash the session_sess args we have already. */
640 new_sess = apr_pcalloc(result_pool, sizeof(*new_sess));
641 new_sess->callbacks = old_sess->callbacks;
642 new_sess->callback_baton = old_sess->callback_baton;
643
644 /* ### Re-use existing FS handle? */
645
646 /* Reuse existing code */
647 SVN_ERR(svn_ra_local__split_URL(&(new_sess->repos),
648 &(new_sess->repos_url),
649 &fs_path,
650 new_session_url,
651 result_pool));
652
653 new_sess->fs_path = svn_stringbuf_create(fs_path, result_pool);
654
655 /* Cache the filesystem object from the repos here for
656 convenience. */
657 new_sess->fs = svn_repos_fs(new_sess->repos);
658
659 /* Ignore FS warnings. */
660 svn_fs_set_warning_func(new_sess->fs, ignore_warnings, NULL);
661
662 /* Cache the repository UUID as well */
663 new_sess->uuid = apr_pstrdup(result_pool, old_sess->uuid);
664
665 new_sess->username = old_sess->username
666 ? apr_pstrdup(result_pool, old_sess->username)
667 : NULL;
668
669 new_sess->useragent = apr_pstrdup(result_pool, old_sess->useragent);
670 new_session->priv = new_sess;
671
672 return SVN_NO_ERROR;
673 }
674
675 static svn_error_t *
svn_ra_local__reparent(svn_ra_session_t * session,const char * url,apr_pool_t * pool)676 svn_ra_local__reparent(svn_ra_session_t *session,
677 const char *url,
678 apr_pool_t *pool)
679 {
680 svn_ra_local__session_baton_t *sess = session->priv;
681 const char *relpath = svn_uri_skip_ancestor(sess->repos_url, url, pool);
682
683 /* If the new URL isn't the same as our repository root URL, then
684 let's ensure that it's some child of it. */
685 if (! relpath)
686 return svn_error_createf
687 (SVN_ERR_RA_ILLEGAL_URL, NULL,
688 _("URL '%s' is not a child of the session's repository root "
689 "URL '%s'"), url, sess->repos_url);
690
691 /* Update our FS_PATH sess member to point to our new
692 relative-URL-turned-absolute-filesystem-path. */
693 svn_stringbuf_set(sess->fs_path,
694 svn_fspath__canonicalize(relpath, pool));
695
696 return SVN_NO_ERROR;
697 }
698
699 static svn_error_t *
svn_ra_local__get_session_url(svn_ra_session_t * session,const char ** url,apr_pool_t * pool)700 svn_ra_local__get_session_url(svn_ra_session_t *session,
701 const char **url,
702 apr_pool_t *pool)
703 {
704 svn_ra_local__session_baton_t *sess = session->priv;
705 *url = svn_path_url_add_component2(sess->repos_url,
706 sess->fs_path->data + 1,
707 pool);
708 return SVN_NO_ERROR;
709 }
710
711 static svn_error_t *
svn_ra_local__get_latest_revnum(svn_ra_session_t * session,svn_revnum_t * latest_revnum,apr_pool_t * pool)712 svn_ra_local__get_latest_revnum(svn_ra_session_t *session,
713 svn_revnum_t *latest_revnum,
714 apr_pool_t *pool)
715 {
716 svn_ra_local__session_baton_t *sess = session->priv;
717 return svn_fs_youngest_rev(latest_revnum, sess->fs, pool);
718 }
719
720 static svn_error_t *
svn_ra_local__get_file_revs(svn_ra_session_t * session,const char * path,svn_revnum_t start,svn_revnum_t end,svn_boolean_t include_merged_revisions,svn_file_rev_handler_t handler,void * handler_baton,apr_pool_t * pool)721 svn_ra_local__get_file_revs(svn_ra_session_t *session,
722 const char *path,
723 svn_revnum_t start,
724 svn_revnum_t end,
725 svn_boolean_t include_merged_revisions,
726 svn_file_rev_handler_t handler,
727 void *handler_baton,
728 apr_pool_t *pool)
729 {
730 svn_ra_local__session_baton_t *sess = session->priv;
731 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
732 return svn_repos_get_file_revs2(sess->repos, abs_path, start, end,
733 include_merged_revisions, NULL, NULL,
734 handler, handler_baton, pool);
735 }
736
737 static svn_error_t *
svn_ra_local__get_dated_revision(svn_ra_session_t * session,svn_revnum_t * revision,apr_time_t tm,apr_pool_t * pool)738 svn_ra_local__get_dated_revision(svn_ra_session_t *session,
739 svn_revnum_t *revision,
740 apr_time_t tm,
741 apr_pool_t *pool)
742 {
743 svn_ra_local__session_baton_t *sess = session->priv;
744 return svn_repos_dated_revision(revision, sess->repos, tm, pool);
745 }
746
747
748 static svn_error_t *
svn_ra_local__change_rev_prop(svn_ra_session_t * session,svn_revnum_t rev,const char * name,const svn_string_t * const * old_value_p,const svn_string_t * value,apr_pool_t * pool)749 svn_ra_local__change_rev_prop(svn_ra_session_t *session,
750 svn_revnum_t rev,
751 const char *name,
752 const svn_string_t *const *old_value_p,
753 const svn_string_t *value,
754 apr_pool_t *pool)
755 {
756 svn_ra_local__session_baton_t *sess = session->priv;
757
758 SVN_ERR(get_username(session, pool));
759 return svn_repos_fs_change_rev_prop4(sess->repos, rev, sess->username,
760 name, old_value_p, value, TRUE, TRUE,
761 NULL, NULL, pool);
762 }
763
764 static svn_error_t *
svn_ra_local__get_uuid(svn_ra_session_t * session,const char ** uuid,apr_pool_t * pool)765 svn_ra_local__get_uuid(svn_ra_session_t *session,
766 const char **uuid,
767 apr_pool_t *pool)
768 {
769 svn_ra_local__session_baton_t *sess = session->priv;
770 *uuid = sess->uuid;
771 return SVN_NO_ERROR;
772 }
773
774 static svn_error_t *
svn_ra_local__get_repos_root(svn_ra_session_t * session,const char ** url,apr_pool_t * pool)775 svn_ra_local__get_repos_root(svn_ra_session_t *session,
776 const char **url,
777 apr_pool_t *pool)
778 {
779 svn_ra_local__session_baton_t *sess = session->priv;
780 *url = sess->repos_url;
781 return SVN_NO_ERROR;
782 }
783
784 static svn_error_t *
svn_ra_local__rev_proplist(svn_ra_session_t * session,svn_revnum_t rev,apr_hash_t ** props,apr_pool_t * pool)785 svn_ra_local__rev_proplist(svn_ra_session_t *session,
786 svn_revnum_t rev,
787 apr_hash_t **props,
788 apr_pool_t *pool)
789 {
790 svn_ra_local__session_baton_t *sess = session->priv;
791 return svn_repos_fs_revision_proplist(props, sess->repos, rev,
792 NULL, NULL, pool);
793 }
794
795 static svn_error_t *
svn_ra_local__rev_prop(svn_ra_session_t * session,svn_revnum_t rev,const char * name,svn_string_t ** value,apr_pool_t * pool)796 svn_ra_local__rev_prop(svn_ra_session_t *session,
797 svn_revnum_t rev,
798 const char *name,
799 svn_string_t **value,
800 apr_pool_t *pool)
801 {
802 svn_ra_local__session_baton_t *sess = session->priv;
803 return svn_repos_fs_revision_prop(value, sess->repos, rev, name,
804 NULL, NULL, pool);
805 }
806
807 struct ccw_baton
808 {
809 svn_commit_callback2_t original_callback;
810 void *original_baton;
811
812 svn_ra_session_t *session;
813 };
814
815 /* Wrapper which populates the repos_root field of the commit_info struct */
816 static svn_error_t *
commit_callback_wrapper(const svn_commit_info_t * commit_info,void * baton,apr_pool_t * scratch_pool)817 commit_callback_wrapper(const svn_commit_info_t *commit_info,
818 void *baton,
819 apr_pool_t *scratch_pool)
820 {
821 struct ccw_baton *ccwb = baton;
822 svn_commit_info_t *ci = svn_commit_info_dup(commit_info, scratch_pool);
823
824 SVN_ERR(svn_ra_local__get_repos_root(ccwb->session, &ci->repos_root,
825 scratch_pool));
826
827 return svn_error_trace(ccwb->original_callback(ci, ccwb->original_baton,
828 scratch_pool));
829 }
830
831
832 /* The repository layer does not correctly fill in REPOS_ROOT in
833 commit_info, as it doesn't know the url that is used to access
834 it. This hooks the callback to fill in the missing pieces. */
835 static void
remap_commit_callback(svn_commit_callback2_t * callback,void ** callback_baton,svn_ra_session_t * session,svn_commit_callback2_t original_callback,void * original_baton,apr_pool_t * result_pool)836 remap_commit_callback(svn_commit_callback2_t *callback,
837 void **callback_baton,
838 svn_ra_session_t *session,
839 svn_commit_callback2_t original_callback,
840 void *original_baton,
841 apr_pool_t *result_pool)
842 {
843 if (original_callback == NULL)
844 {
845 *callback = NULL;
846 *callback_baton = NULL;
847 }
848 else
849 {
850 /* Allocate this in RESULT_POOL, since the callback will be called
851 long after this function has returned. */
852 struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb));
853
854 ccwb->session = session;
855 ccwb->original_callback = original_callback;
856 ccwb->original_baton = original_baton;
857
858 *callback = commit_callback_wrapper;
859 *callback_baton = ccwb;
860 }
861 }
862
863 static svn_error_t *
svn_ra_local__get_commit_editor(svn_ra_session_t * session,const svn_delta_editor_t ** editor,void ** edit_baton,apr_hash_t * revprop_table,svn_commit_callback2_t callback,void * callback_baton,apr_hash_t * lock_tokens,svn_boolean_t keep_locks,apr_pool_t * pool)864 svn_ra_local__get_commit_editor(svn_ra_session_t *session,
865 const svn_delta_editor_t **editor,
866 void **edit_baton,
867 apr_hash_t *revprop_table,
868 svn_commit_callback2_t callback,
869 void *callback_baton,
870 apr_hash_t *lock_tokens,
871 svn_boolean_t keep_locks,
872 apr_pool_t *pool)
873 {
874 svn_ra_local__session_baton_t *sess = session->priv;
875 struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb));
876
877 /* Set repos_root_url in commit info */
878 remap_commit_callback(&callback, &callback_baton, session,
879 callback, callback_baton, pool);
880
881 /* Prepare the baton for deltify_etc() */
882 deb->fs = sess->fs;
883 deb->repos = sess->repos;
884 deb->fspath_base = sess->fs_path->data;
885 if (! keep_locks)
886 deb->lock_tokens = lock_tokens;
887 else
888 deb->lock_tokens = NULL;
889 deb->commit_cb = callback;
890 deb->commit_baton = callback_baton;
891
892 SVN_ERR(get_username(session, pool));
893
894 /* If there are lock tokens to add, do so. */
895 SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens,
896 session->pool, pool));
897
898 /* Copy the revprops table so we can add the username. */
899 revprop_table = apr_hash_copy(pool, revprop_table);
900 svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
901 svn_string_create(sess->username, pool));
902 svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION,
903 svn_string_create(SVN_VER_NUMBER, pool));
904 svn_hash_sets(revprop_table, SVN_PROP_TXN_USER_AGENT,
905 svn_string_create(sess->useragent, pool));
906
907 /* Get the repos commit-editor */
908 return svn_repos_get_commit_editor5
909 (editor, edit_baton, sess->repos, NULL,
910 svn_path_uri_decode(sess->repos_url, pool), sess->fs_path->data,
911 revprop_table, deltify_etc, deb, NULL, NULL, pool);
912 }
913
914
915 /* Implements svn_repos_mergeinfo_receiver_t.
916 * It add MERGEINFO for PATH to the svn_mergeinfo_catalog_t BATON.
917 */
918 static svn_error_t *
mergeinfo_receiver(const char * path,svn_mergeinfo_t mergeinfo,void * baton,apr_pool_t * scratch_pool)919 mergeinfo_receiver(const char *path,
920 svn_mergeinfo_t mergeinfo,
921 void *baton,
922 apr_pool_t *scratch_pool)
923 {
924 svn_mergeinfo_catalog_t catalog = baton;
925 apr_pool_t *result_pool = apr_hash_pool_get(catalog);
926 apr_size_t len = strlen(path);
927
928 apr_hash_set(catalog,
929 apr_pstrmemdup(result_pool, path, len),
930 len,
931 svn_mergeinfo_dup(mergeinfo, result_pool));
932
933 return SVN_NO_ERROR;
934 }
935
936
937 static svn_error_t *
svn_ra_local__get_mergeinfo(svn_ra_session_t * session,svn_mergeinfo_catalog_t * catalog,const apr_array_header_t * paths,svn_revnum_t revision,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,apr_pool_t * pool)938 svn_ra_local__get_mergeinfo(svn_ra_session_t *session,
939 svn_mergeinfo_catalog_t *catalog,
940 const apr_array_header_t *paths,
941 svn_revnum_t revision,
942 svn_mergeinfo_inheritance_t inherit,
943 svn_boolean_t include_descendants,
944 apr_pool_t *pool)
945 {
946 svn_ra_local__session_baton_t *sess = session->priv;
947 svn_mergeinfo_catalog_t tmp_catalog = svn_hash__make(pool);
948 int i;
949 apr_array_header_t *abs_paths =
950 apr_array_make(pool, 0, sizeof(const char *));
951
952 for (i = 0; i < paths->nelts; i++)
953 {
954 const char *relative_path = APR_ARRAY_IDX(paths, i, const char *);
955 APR_ARRAY_PUSH(abs_paths, const char *) =
956 svn_fspath__join(sess->fs_path->data, relative_path, pool);
957 }
958
959 SVN_ERR(svn_repos_fs_get_mergeinfo2(sess->repos, abs_paths, revision,
960 inherit, include_descendants,
961 NULL, NULL,
962 mergeinfo_receiver, tmp_catalog,
963 pool));
964 if (apr_hash_count(tmp_catalog) > 0)
965 SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(catalog,
966 tmp_catalog,
967 sess->fs_path->data,
968 pool));
969 else
970 *catalog = NULL;
971
972 return SVN_NO_ERROR;
973 }
974
975
976 static svn_error_t *
svn_ra_local__do_update(svn_ra_session_t * session,const svn_ra_reporter3_t ** reporter,void ** report_baton,svn_revnum_t update_revision,const char * update_target,svn_depth_t depth,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry,const svn_delta_editor_t * update_editor,void * update_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)977 svn_ra_local__do_update(svn_ra_session_t *session,
978 const svn_ra_reporter3_t **reporter,
979 void **report_baton,
980 svn_revnum_t update_revision,
981 const char *update_target,
982 svn_depth_t depth,
983 svn_boolean_t send_copyfrom_args,
984 svn_boolean_t ignore_ancestry,
985 const svn_delta_editor_t *update_editor,
986 void *update_baton,
987 apr_pool_t *result_pool,
988 apr_pool_t *scratch_pool)
989 {
990 return make_reporter(session,
991 reporter,
992 report_baton,
993 update_revision,
994 update_target,
995 NULL,
996 TRUE,
997 depth,
998 send_copyfrom_args,
999 ignore_ancestry,
1000 update_editor,
1001 update_baton,
1002 result_pool, scratch_pool);
1003 }
1004
1005
1006 static svn_error_t *
svn_ra_local__do_switch(svn_ra_session_t * session,const svn_ra_reporter3_t ** reporter,void ** report_baton,svn_revnum_t update_revision,const char * update_target,svn_depth_t depth,const char * switch_url,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry,const svn_delta_editor_t * update_editor,void * update_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1007 svn_ra_local__do_switch(svn_ra_session_t *session,
1008 const svn_ra_reporter3_t **reporter,
1009 void **report_baton,
1010 svn_revnum_t update_revision,
1011 const char *update_target,
1012 svn_depth_t depth,
1013 const char *switch_url,
1014 svn_boolean_t send_copyfrom_args,
1015 svn_boolean_t ignore_ancestry,
1016 const svn_delta_editor_t *update_editor,
1017 void *update_baton,
1018 apr_pool_t *result_pool,
1019 apr_pool_t *scratch_pool)
1020 {
1021 return make_reporter(session,
1022 reporter,
1023 report_baton,
1024 update_revision,
1025 update_target,
1026 switch_url,
1027 TRUE /* text_deltas */,
1028 depth,
1029 send_copyfrom_args,
1030 ignore_ancestry,
1031 update_editor,
1032 update_baton,
1033 result_pool, scratch_pool);
1034 }
1035
1036
1037 static svn_error_t *
svn_ra_local__do_status(svn_ra_session_t * session,const svn_ra_reporter3_t ** reporter,void ** report_baton,const char * status_target,svn_revnum_t revision,svn_depth_t depth,const svn_delta_editor_t * status_editor,void * status_baton,apr_pool_t * pool)1038 svn_ra_local__do_status(svn_ra_session_t *session,
1039 const svn_ra_reporter3_t **reporter,
1040 void **report_baton,
1041 const char *status_target,
1042 svn_revnum_t revision,
1043 svn_depth_t depth,
1044 const svn_delta_editor_t *status_editor,
1045 void *status_baton,
1046 apr_pool_t *pool)
1047 {
1048 return make_reporter(session,
1049 reporter,
1050 report_baton,
1051 revision,
1052 status_target,
1053 NULL,
1054 FALSE,
1055 depth,
1056 FALSE,
1057 FALSE,
1058 status_editor,
1059 status_baton,
1060 pool, pool);
1061 }
1062
1063
1064 static svn_error_t *
svn_ra_local__do_diff(svn_ra_session_t * session,const svn_ra_reporter3_t ** reporter,void ** report_baton,svn_revnum_t update_revision,const char * update_target,svn_depth_t depth,svn_boolean_t ignore_ancestry,svn_boolean_t text_deltas,const char * switch_url,const svn_delta_editor_t * update_editor,void * update_baton,apr_pool_t * pool)1065 svn_ra_local__do_diff(svn_ra_session_t *session,
1066 const svn_ra_reporter3_t **reporter,
1067 void **report_baton,
1068 svn_revnum_t update_revision,
1069 const char *update_target,
1070 svn_depth_t depth,
1071 svn_boolean_t ignore_ancestry,
1072 svn_boolean_t text_deltas,
1073 const char *switch_url,
1074 const svn_delta_editor_t *update_editor,
1075 void *update_baton,
1076 apr_pool_t *pool)
1077 {
1078 return make_reporter(session,
1079 reporter,
1080 report_baton,
1081 update_revision,
1082 update_target,
1083 switch_url,
1084 text_deltas,
1085 depth,
1086 FALSE,
1087 ignore_ancestry,
1088 update_editor,
1089 update_baton,
1090 pool, pool);
1091 }
1092
1093
1094 struct log_baton
1095 {
1096 svn_ra_local__session_baton_t *sess;
1097 svn_log_entry_receiver_t real_cb;
1098 void *real_baton;
1099 };
1100
1101 static svn_error_t *
log_receiver_wrapper(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)1102 log_receiver_wrapper(void *baton,
1103 svn_log_entry_t *log_entry,
1104 apr_pool_t *pool)
1105 {
1106 struct log_baton *b = baton;
1107 svn_ra_local__session_baton_t *sess = b->sess;
1108
1109 if (sess->callbacks->cancel_func)
1110 SVN_ERR((sess->callbacks->cancel_func)(sess->callback_baton));
1111
1112 /* For consistency with the other RA layers, replace an empty
1113 changed-paths hash with a NULL one.
1114
1115 ### Should this be done by svn_ra_get_log2() instead, then? */
1116 if ((log_entry->changed_paths2)
1117 && (apr_hash_count(log_entry->changed_paths2) == 0))
1118 {
1119 log_entry->changed_paths = NULL;
1120 log_entry->changed_paths2 = NULL;
1121 }
1122
1123 return b->real_cb(b->real_baton, log_entry, pool);
1124 }
1125
1126
1127 static svn_error_t *
svn_ra_local__get_log(svn_ra_session_t * session,const apr_array_header_t * paths,svn_revnum_t start,svn_revnum_t end,int limit,svn_boolean_t discover_changed_paths,svn_boolean_t strict_node_history,svn_boolean_t include_merged_revisions,const apr_array_header_t * revprops,svn_log_entry_receiver_t receiver,void * receiver_baton,apr_pool_t * pool)1128 svn_ra_local__get_log(svn_ra_session_t *session,
1129 const apr_array_header_t *paths,
1130 svn_revnum_t start,
1131 svn_revnum_t end,
1132 int limit,
1133 svn_boolean_t discover_changed_paths,
1134 svn_boolean_t strict_node_history,
1135 svn_boolean_t include_merged_revisions,
1136 const apr_array_header_t *revprops,
1137 svn_log_entry_receiver_t receiver,
1138 void *receiver_baton,
1139 apr_pool_t *pool)
1140 {
1141 svn_ra_local__session_baton_t *sess = session->priv;
1142 struct log_baton lb;
1143 apr_array_header_t *abs_paths =
1144 apr_array_make(pool, 0, sizeof(const char *));
1145
1146 if (paths)
1147 {
1148 int i;
1149
1150 for (i = 0; i < paths->nelts; i++)
1151 {
1152 const char *relative_path = APR_ARRAY_IDX(paths, i, const char *);
1153 APR_ARRAY_PUSH(abs_paths, const char *) =
1154 svn_fspath__join(sess->fs_path->data, relative_path, pool);
1155 }
1156 }
1157
1158 lb.real_cb = receiver;
1159 lb.real_baton = receiver_baton;
1160 lb.sess = sess;
1161 receiver = log_receiver_wrapper;
1162 receiver_baton = &lb;
1163
1164 return svn_repos__get_logs_compat(sess->repos,
1165 abs_paths,
1166 start,
1167 end,
1168 limit,
1169 discover_changed_paths,
1170 strict_node_history,
1171 include_merged_revisions,
1172 revprops,
1173 NULL, NULL,
1174 receiver,
1175 receiver_baton,
1176 pool);
1177 }
1178
1179
1180 static svn_error_t *
svn_ra_local__do_check_path(svn_ra_session_t * session,const char * path,svn_revnum_t revision,svn_node_kind_t * kind,apr_pool_t * pool)1181 svn_ra_local__do_check_path(svn_ra_session_t *session,
1182 const char *path,
1183 svn_revnum_t revision,
1184 svn_node_kind_t *kind,
1185 apr_pool_t *pool)
1186 {
1187 svn_ra_local__session_baton_t *sess = session->priv;
1188 svn_fs_root_t *root;
1189 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1190
1191 if (! SVN_IS_VALID_REVNUM(revision))
1192 SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool));
1193 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1194 return svn_fs_check_path(kind, root, abs_path, pool);
1195 }
1196
1197
1198 static svn_error_t *
svn_ra_local__stat(svn_ra_session_t * session,const char * path,svn_revnum_t revision,svn_dirent_t ** dirent,apr_pool_t * pool)1199 svn_ra_local__stat(svn_ra_session_t *session,
1200 const char *path,
1201 svn_revnum_t revision,
1202 svn_dirent_t **dirent,
1203 apr_pool_t *pool)
1204 {
1205 svn_ra_local__session_baton_t *sess = session->priv;
1206 svn_fs_root_t *root;
1207 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1208
1209 if (! SVN_IS_VALID_REVNUM(revision))
1210 SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool));
1211 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1212
1213 return svn_repos_stat(dirent, root, abs_path, pool);
1214 }
1215
1216
1217
1218
1219 /* Obtain the properties for a node, including its 'entry props */
1220 static svn_error_t *
get_node_props(apr_hash_t ** props,svn_fs_root_t * root,const char * path,const char * uuid,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1221 get_node_props(apr_hash_t **props,
1222 svn_fs_root_t *root,
1223 const char *path,
1224 const char *uuid,
1225 apr_pool_t *result_pool,
1226 apr_pool_t *scratch_pool)
1227 {
1228 svn_revnum_t cmt_rev;
1229 const char *cmt_date, *cmt_author;
1230
1231 /* Create a hash with props attached to the fs node. */
1232 SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool));
1233
1234 /* Now add some non-tweakable metadata to the hash as well... */
1235
1236 /* The so-called 'entryprops' with info about CR & friends. */
1237 SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date,
1238 &cmt_author, root, path,
1239 scratch_pool));
1240
1241 svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV,
1242 svn_string_createf(result_pool, "%ld", cmt_rev));
1243 svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, cmt_date ?
1244 svn_string_create(cmt_date, result_pool) : NULL);
1245 svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, cmt_author ?
1246 svn_string_create(cmt_author, result_pool) : NULL);
1247 svn_hash_sets(*props, SVN_PROP_ENTRY_UUID,
1248 svn_string_create(uuid, result_pool));
1249
1250 /* We have no 'wcprops' in ra_local, but might someday. */
1251
1252 return SVN_NO_ERROR;
1253 }
1254
1255
1256 /* Getting just one file. */
1257 static svn_error_t *
svn_ra_local__get_file(svn_ra_session_t * session,const char * path,svn_revnum_t revision,svn_stream_t * stream,svn_revnum_t * fetched_rev,apr_hash_t ** props,apr_pool_t * pool)1258 svn_ra_local__get_file(svn_ra_session_t *session,
1259 const char *path,
1260 svn_revnum_t revision,
1261 svn_stream_t *stream,
1262 svn_revnum_t *fetched_rev,
1263 apr_hash_t **props,
1264 apr_pool_t *pool)
1265 {
1266 svn_fs_root_t *root;
1267 svn_stream_t *contents;
1268 svn_revnum_t youngest_rev;
1269 svn_ra_local__session_baton_t *sess = session->priv;
1270 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1271 svn_node_kind_t node_kind;
1272
1273 /* Open the revision's root. */
1274 if (! SVN_IS_VALID_REVNUM(revision))
1275 {
1276 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool));
1277 SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool));
1278 if (fetched_rev != NULL)
1279 *fetched_rev = youngest_rev;
1280 }
1281 else
1282 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1283
1284 SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, pool));
1285 if (node_kind == svn_node_none)
1286 {
1287 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
1288 _("'%s' path not found"), abs_path);
1289 }
1290 else if (node_kind != svn_node_file)
1291 {
1292 return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
1293 _("'%s' is not a file"), abs_path);
1294 }
1295
1296 if (stream)
1297 {
1298 /* Get a stream representing the file's contents. */
1299 SVN_ERR(svn_fs_file_contents(&contents, root, abs_path, pool));
1300
1301 /* Now push data from the fs stream back at the caller's stream.
1302 Note that this particular RA layer does not computing a
1303 checksum as we go, and confirming it against the repository's
1304 checksum when done. That's because it calls
1305 svn_fs_file_contents() directly, which already checks the
1306 stored checksum, and all we're doing here is writing bytes in
1307 a loop. Truly, Nothing Can Go Wrong :-). But RA layers that
1308 go over a network should confirm the checksum.
1309
1310 Note: we are not supposed to close the passed-in stream, so
1311 disown the thing.
1312 */
1313 SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(stream, pool),
1314 sess->callbacks
1315 ? sess->callbacks->cancel_func : NULL,
1316 sess->callback_baton,
1317 pool));
1318 }
1319
1320 /* Handle props if requested. */
1321 if (props)
1322 SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool));
1323
1324 return SVN_NO_ERROR;
1325 }
1326
1327
1328
1329 /* Getting a directory's entries */
1330 static svn_error_t *
svn_ra_local__get_dir(svn_ra_session_t * session,apr_hash_t ** dirents,svn_revnum_t * fetched_rev,apr_hash_t ** props,const char * path,svn_revnum_t revision,apr_uint32_t dirent_fields,apr_pool_t * pool)1331 svn_ra_local__get_dir(svn_ra_session_t *session,
1332 apr_hash_t **dirents,
1333 svn_revnum_t *fetched_rev,
1334 apr_hash_t **props,
1335 const char *path,
1336 svn_revnum_t revision,
1337 apr_uint32_t dirent_fields,
1338 apr_pool_t *pool)
1339 {
1340 svn_fs_root_t *root;
1341 svn_revnum_t youngest_rev;
1342 apr_hash_t *entries;
1343 apr_hash_index_t *hi;
1344 svn_ra_local__session_baton_t *sess = session->priv;
1345 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1346
1347 /* Open the revision's root. */
1348 if (! SVN_IS_VALID_REVNUM(revision))
1349 {
1350 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool));
1351 SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool));
1352 if (fetched_rev != NULL)
1353 *fetched_rev = youngest_rev;
1354 }
1355 else
1356 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1357
1358 if (dirents)
1359 {
1360 apr_pool_t *iterpool = svn_pool_create(pool);
1361 /* Get the dir's entries. */
1362 SVN_ERR(svn_fs_dir_entries(&entries, root, abs_path, pool));
1363
1364 /* Loop over the fs dirents, and build a hash of general
1365 svn_dirent_t's. */
1366 *dirents = apr_hash_make(pool);
1367 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1368 {
1369 const void *key;
1370 void *val;
1371 const char *datestring, *entryname, *fullpath;
1372 svn_fs_dirent_t *fs_entry;
1373 svn_dirent_t *entry = svn_dirent_create(pool);
1374
1375 svn_pool_clear(iterpool);
1376
1377 apr_hash_this(hi, &key, NULL, &val);
1378 entryname = (const char *) key;
1379 fs_entry = (svn_fs_dirent_t *) val;
1380
1381 fullpath = svn_dirent_join(abs_path, entryname, iterpool);
1382
1383 if (dirent_fields & SVN_DIRENT_KIND)
1384 {
1385 /* node kind */
1386 entry->kind = fs_entry->kind;
1387 }
1388
1389 if (dirent_fields & SVN_DIRENT_SIZE)
1390 {
1391 /* size */
1392 if (fs_entry->kind == svn_node_dir)
1393 entry->size = SVN_INVALID_FILESIZE;
1394 else
1395 SVN_ERR(svn_fs_file_length(&(entry->size), root,
1396 fullpath, iterpool));
1397 }
1398
1399 if (dirent_fields & SVN_DIRENT_HAS_PROPS)
1400 {
1401 /* has_props? */
1402 SVN_ERR(svn_fs_node_has_props(&entry->has_props,
1403 root, fullpath,
1404 iterpool));
1405 }
1406
1407 if ((dirent_fields & SVN_DIRENT_TIME)
1408 || (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
1409 || (dirent_fields & SVN_DIRENT_CREATED_REV))
1410 {
1411 /* created_rev & friends */
1412 SVN_ERR(svn_repos_get_committed_info(&(entry->created_rev),
1413 &datestring,
1414 &(entry->last_author),
1415 root, fullpath, iterpool));
1416 if (datestring)
1417 SVN_ERR(svn_time_from_cstring(&(entry->time), datestring,
1418 pool));
1419 if (entry->last_author)
1420 entry->last_author = apr_pstrdup(pool, entry->last_author);
1421 }
1422
1423 /* Store. */
1424 svn_hash_sets(*dirents, entryname, entry);
1425 }
1426 svn_pool_destroy(iterpool);
1427 }
1428
1429 /* Handle props if requested. */
1430 if (props)
1431 SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool));
1432
1433 return SVN_NO_ERROR;
1434 }
1435
1436
1437 static svn_error_t *
svn_ra_local__get_locations(svn_ra_session_t * session,apr_hash_t ** locations,const char * path,svn_revnum_t peg_revision,const apr_array_header_t * location_revisions,apr_pool_t * pool)1438 svn_ra_local__get_locations(svn_ra_session_t *session,
1439 apr_hash_t **locations,
1440 const char *path,
1441 svn_revnum_t peg_revision,
1442 const apr_array_header_t *location_revisions,
1443 apr_pool_t *pool)
1444 {
1445 svn_ra_local__session_baton_t *sess = session->priv;
1446 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1447 return svn_repos_trace_node_locations(sess->fs, locations, abs_path,
1448 peg_revision, location_revisions,
1449 NULL, NULL, pool);
1450 }
1451
1452
1453 static svn_error_t *
svn_ra_local__get_location_segments(svn_ra_session_t * session,const char * path,svn_revnum_t peg_revision,svn_revnum_t start_rev,svn_revnum_t end_rev,svn_location_segment_receiver_t receiver,void * receiver_baton,apr_pool_t * pool)1454 svn_ra_local__get_location_segments(svn_ra_session_t *session,
1455 const char *path,
1456 svn_revnum_t peg_revision,
1457 svn_revnum_t start_rev,
1458 svn_revnum_t end_rev,
1459 svn_location_segment_receiver_t receiver,
1460 void *receiver_baton,
1461 apr_pool_t *pool)
1462 {
1463 svn_ra_local__session_baton_t *sess = session->priv;
1464 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1465 return svn_repos_node_location_segments(sess->repos, abs_path,
1466 peg_revision, start_rev, end_rev,
1467 receiver, receiver_baton,
1468 NULL, NULL, pool);
1469 }
1470
1471 struct lock_baton_t {
1472 svn_ra_lock_callback_t lock_func;
1473 void *lock_baton;
1474 const char *fs_path;
1475 svn_boolean_t is_lock;
1476 svn_error_t *cb_err;
1477 };
1478
1479 /* Implements svn_fs_lock_callback_t. Used by svn_ra_local__lock and
1480 svn_ra_local__unlock to forward to supplied callback and record any
1481 callback error. */
1482 static svn_error_t *
lock_cb(void * lock_baton,const char * path,const svn_lock_t * lock,svn_error_t * fs_err,apr_pool_t * pool)1483 lock_cb(void *lock_baton,
1484 const char *path,
1485 const svn_lock_t *lock,
1486 svn_error_t *fs_err,
1487 apr_pool_t *pool)
1488 {
1489 struct lock_baton_t *b = lock_baton;
1490
1491 if (b && !b->cb_err && b->lock_func)
1492 {
1493 path = svn_fspath__skip_ancestor(b->fs_path, path);
1494 b->cb_err = b->lock_func(b->lock_baton, path, b->is_lock, lock, fs_err,
1495 pool);
1496 }
1497
1498 return SVN_NO_ERROR;
1499 }
1500
1501 static svn_error_t *
svn_ra_local__lock(svn_ra_session_t * session,apr_hash_t * path_revs,const char * comment,svn_boolean_t force,svn_ra_lock_callback_t lock_func,void * lock_baton,apr_pool_t * pool)1502 svn_ra_local__lock(svn_ra_session_t *session,
1503 apr_hash_t *path_revs,
1504 const char *comment,
1505 svn_boolean_t force,
1506 svn_ra_lock_callback_t lock_func,
1507 void *lock_baton,
1508 apr_pool_t *pool)
1509 {
1510 svn_ra_local__session_baton_t *sess = session->priv;
1511 apr_hash_t *targets = apr_hash_make(pool);
1512 apr_hash_index_t *hi;
1513 svn_error_t *err;
1514 struct lock_baton_t baton = {0};
1515
1516 /* A username is absolutely required to lock a path. */
1517 SVN_ERR(get_username(session, pool));
1518
1519 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
1520 {
1521 const char *abs_path = svn_fspath__join(sess->fs_path->data,
1522 apr_hash_this_key(hi), pool);
1523 svn_revnum_t current_rev = *(svn_revnum_t *)apr_hash_this_val(hi);
1524 svn_fs_lock_target_t *target = svn_fs_lock_target_create(NULL,
1525 current_rev,
1526 pool);
1527
1528 svn_hash_sets(targets, abs_path, target);
1529 }
1530
1531 baton.lock_func = lock_func;
1532 baton.lock_baton = lock_baton;
1533 baton.fs_path = sess->fs_path->data;
1534 baton.is_lock = TRUE;
1535 baton.cb_err = SVN_NO_ERROR;
1536
1537 err = svn_repos_fs_lock_many(sess->repos, targets, comment,
1538 FALSE /* not DAV comment */,
1539 0 /* no expiration */, force,
1540 lock_cb, &baton,
1541 pool, pool);
1542
1543 if (err && baton.cb_err)
1544 svn_error_compose(err, baton.cb_err);
1545 else if (!err)
1546 err = baton.cb_err;
1547
1548 return svn_error_trace(err);
1549 }
1550
1551
1552 static svn_error_t *
svn_ra_local__unlock(svn_ra_session_t * session,apr_hash_t * path_tokens,svn_boolean_t force,svn_ra_lock_callback_t lock_func,void * lock_baton,apr_pool_t * pool)1553 svn_ra_local__unlock(svn_ra_session_t *session,
1554 apr_hash_t *path_tokens,
1555 svn_boolean_t force,
1556 svn_ra_lock_callback_t lock_func,
1557 void *lock_baton,
1558 apr_pool_t *pool)
1559 {
1560 svn_ra_local__session_baton_t *sess = session->priv;
1561 apr_hash_t *targets = apr_hash_make(pool);
1562 apr_hash_index_t *hi;
1563 svn_error_t *err;
1564 struct lock_baton_t baton = {0};
1565
1566 /* A username is absolutely required to unlock a path. */
1567 SVN_ERR(get_username(session, pool));
1568
1569 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
1570 {
1571 const char *abs_path = svn_fspath__join(sess->fs_path->data,
1572 apr_hash_this_key(hi), pool);
1573 const char *token = apr_hash_this_val(hi);
1574
1575 svn_hash_sets(targets, abs_path, token);
1576 }
1577
1578 baton.lock_func = lock_func;
1579 baton.lock_baton = lock_baton;
1580 baton.fs_path = sess->fs_path->data;
1581 baton.is_lock = FALSE;
1582 baton.cb_err = SVN_NO_ERROR;
1583
1584 err = svn_repos_fs_unlock_many(sess->repos, targets, force, lock_cb, &baton,
1585 pool, pool);
1586
1587 if (err && baton.cb_err)
1588 svn_error_compose(err, baton.cb_err);
1589 else if (!err)
1590 err = baton.cb_err;
1591
1592 return svn_error_trace(err);
1593 }
1594
1595
1596
1597 static svn_error_t *
svn_ra_local__get_lock(svn_ra_session_t * session,svn_lock_t ** lock,const char * path,apr_pool_t * pool)1598 svn_ra_local__get_lock(svn_ra_session_t *session,
1599 svn_lock_t **lock,
1600 const char *path,
1601 apr_pool_t *pool)
1602 {
1603 svn_ra_local__session_baton_t *sess = session->priv;
1604 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1605 return svn_fs_get_lock(lock, sess->fs, abs_path, pool);
1606 }
1607
1608
1609
1610 static svn_error_t *
svn_ra_local__get_locks(svn_ra_session_t * session,apr_hash_t ** locks,const char * path,svn_depth_t depth,apr_pool_t * pool)1611 svn_ra_local__get_locks(svn_ra_session_t *session,
1612 apr_hash_t **locks,
1613 const char *path,
1614 svn_depth_t depth,
1615 apr_pool_t *pool)
1616 {
1617 svn_ra_local__session_baton_t *sess = session->priv;
1618 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1619
1620 /* Kinda silly to call the repos wrapper, since we have no authz
1621 func to give it. But heck, why not. */
1622 return svn_repos_fs_get_locks2(locks, sess->repos, abs_path, depth,
1623 NULL, NULL, pool);
1624 }
1625
1626
1627 static svn_error_t *
svn_ra_local__replay(svn_ra_session_t * session,svn_revnum_t revision,svn_revnum_t low_water_mark,svn_boolean_t send_deltas,const svn_delta_editor_t * editor,void * edit_baton,apr_pool_t * pool)1628 svn_ra_local__replay(svn_ra_session_t *session,
1629 svn_revnum_t revision,
1630 svn_revnum_t low_water_mark,
1631 svn_boolean_t send_deltas,
1632 const svn_delta_editor_t *editor,
1633 void *edit_baton,
1634 apr_pool_t *pool)
1635 {
1636 svn_ra_local__session_baton_t *sess = session->priv;
1637 svn_fs_root_t *root;
1638
1639 SVN_ERR(svn_fs_revision_root(&root, svn_repos_fs(sess->repos),
1640 revision, pool));
1641 return svn_repos_replay2(root, sess->fs_path->data, low_water_mark,
1642 send_deltas, editor, edit_baton, NULL, NULL,
1643 pool);
1644 }
1645
1646
1647 static svn_error_t *
svn_ra_local__replay_range(svn_ra_session_t * session,svn_revnum_t start_revision,svn_revnum_t end_revision,svn_revnum_t low_water_mark,svn_boolean_t send_deltas,svn_ra_replay_revstart_callback_t revstart_func,svn_ra_replay_revfinish_callback_t revfinish_func,void * replay_baton,apr_pool_t * pool)1648 svn_ra_local__replay_range(svn_ra_session_t *session,
1649 svn_revnum_t start_revision,
1650 svn_revnum_t end_revision,
1651 svn_revnum_t low_water_mark,
1652 svn_boolean_t send_deltas,
1653 svn_ra_replay_revstart_callback_t revstart_func,
1654 svn_ra_replay_revfinish_callback_t revfinish_func,
1655 void *replay_baton,
1656 apr_pool_t *pool)
1657 {
1658 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1659 }
1660
1661
1662 static svn_error_t *
svn_ra_local__has_capability(svn_ra_session_t * session,svn_boolean_t * has,const char * capability,apr_pool_t * pool)1663 svn_ra_local__has_capability(svn_ra_session_t *session,
1664 svn_boolean_t *has,
1665 const char *capability,
1666 apr_pool_t *pool)
1667 {
1668 svn_ra_local__session_baton_t *sess = session->priv;
1669
1670 if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0
1671 || strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0
1672 || strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0
1673 || strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0
1674 || strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0
1675 || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0
1676 || strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0
1677 || strcmp(capability, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) == 0
1678 || strcmp(capability, SVN_RA_CAPABILITY_LIST) == 0
1679 )
1680 {
1681 *has = TRUE;
1682 }
1683 else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
1684 {
1685 /* With mergeinfo, the code's capabilities may not reflect the
1686 repository's, so inquire further. */
1687 SVN_ERR(svn_repos_has_capability(sess->repos, has,
1688 SVN_REPOS_CAPABILITY_MERGEINFO,
1689 pool));
1690 }
1691 else /* Don't know any other capabilities, so error. */
1692 {
1693 return svn_error_createf
1694 (SVN_ERR_UNKNOWN_CAPABILITY, NULL,
1695 _("Don't know anything about capability '%s'"), capability);
1696 }
1697
1698 return SVN_NO_ERROR;
1699 }
1700
1701 static svn_error_t *
svn_ra_local__get_deleted_rev(svn_ra_session_t * session,const char * path,svn_revnum_t peg_revision,svn_revnum_t end_revision,svn_revnum_t * revision_deleted,apr_pool_t * pool)1702 svn_ra_local__get_deleted_rev(svn_ra_session_t *session,
1703 const char *path,
1704 svn_revnum_t peg_revision,
1705 svn_revnum_t end_revision,
1706 svn_revnum_t *revision_deleted,
1707 apr_pool_t *pool)
1708 {
1709 svn_ra_local__session_baton_t *sess = session->priv;
1710 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1711
1712 SVN_ERR(svn_repos_deleted_rev(sess->fs,
1713 abs_path,
1714 peg_revision,
1715 end_revision,
1716 revision_deleted,
1717 pool));
1718
1719 return SVN_NO_ERROR;
1720 }
1721
1722 static svn_error_t *
svn_ra_local__get_inherited_props(svn_ra_session_t * session,apr_array_header_t ** iprops,const char * path,svn_revnum_t revision,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1723 svn_ra_local__get_inherited_props(svn_ra_session_t *session,
1724 apr_array_header_t **iprops,
1725 const char *path,
1726 svn_revnum_t revision,
1727 apr_pool_t *result_pool,
1728 apr_pool_t *scratch_pool)
1729 {
1730 svn_fs_root_t *root;
1731 svn_ra_local__session_baton_t *sess = session->priv;
1732 const char *abs_path = svn_fspath__join(sess->fs_path->data, path,
1733 scratch_pool);
1734 svn_node_kind_t node_kind;
1735
1736 /* Open the revision's root. */
1737 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, scratch_pool));
1738
1739 SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, scratch_pool));
1740 if (node_kind == svn_node_none)
1741 {
1742 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
1743 _("'%s' path not found"), abs_path);
1744 }
1745
1746 return svn_error_trace(
1747 svn_repos_fs_get_inherited_props(iprops, root, abs_path,
1748 NULL /* propname */,
1749 NULL, NULL /* auth */,
1750 result_pool, scratch_pool));
1751 }
1752
1753 static svn_error_t *
svn_ra_local__register_editor_shim_callbacks(svn_ra_session_t * session,svn_delta_shim_callbacks_t * callbacks)1754 svn_ra_local__register_editor_shim_callbacks(svn_ra_session_t *session,
1755 svn_delta_shim_callbacks_t *callbacks)
1756 {
1757 /* This is currenly a no-op, since we don't provide our own editor, just
1758 use the one the libsvn_repos hands back to us. */
1759 return SVN_NO_ERROR;
1760 }
1761
1762
1763 static svn_error_t *
svn_ra_local__get_commit_ev2(svn_editor_t ** editor,svn_ra_session_t * session,apr_hash_t * revprops,svn_commit_callback2_t commit_cb,void * commit_baton,apr_hash_t * lock_tokens,svn_boolean_t keep_locks,svn_ra__provide_base_cb_t provide_base_cb,svn_ra__provide_props_cb_t provide_props_cb,svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,void * cb_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1764 svn_ra_local__get_commit_ev2(svn_editor_t **editor,
1765 svn_ra_session_t *session,
1766 apr_hash_t *revprops,
1767 svn_commit_callback2_t commit_cb,
1768 void *commit_baton,
1769 apr_hash_t *lock_tokens,
1770 svn_boolean_t keep_locks,
1771 svn_ra__provide_base_cb_t provide_base_cb,
1772 svn_ra__provide_props_cb_t provide_props_cb,
1773 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
1774 void *cb_baton,
1775 svn_cancel_func_t cancel_func,
1776 void *cancel_baton,
1777 apr_pool_t *result_pool,
1778 apr_pool_t *scratch_pool)
1779 {
1780 svn_ra_local__session_baton_t *sess = session->priv;
1781 struct deltify_etc_baton *deb = apr_palloc(result_pool, sizeof(*deb));
1782
1783 remap_commit_callback(&commit_cb, &commit_baton, session,
1784 commit_cb, commit_baton, result_pool);
1785
1786 /* NOTE: the RA callbacks are ignored. We pass everything directly to
1787 the REPOS editor. */
1788
1789 /* Prepare the baton for deltify_etc() */
1790 deb->fs = sess->fs;
1791 deb->repos = sess->repos;
1792 deb->fspath_base = sess->fs_path->data;
1793 if (! keep_locks)
1794 deb->lock_tokens = lock_tokens;
1795 else
1796 deb->lock_tokens = NULL;
1797 deb->commit_cb = commit_cb;
1798 deb->commit_baton = commit_baton;
1799
1800 /* Ensure there is a username (and an FS access context) associated with
1801 the session and its FS handle. */
1802 SVN_ERR(get_username(session, scratch_pool));
1803
1804 /* If there are lock tokens to add, do so. */
1805 SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens,
1806 session->pool, scratch_pool));
1807
1808 /* Copy the REVPROPS and insert the author/username. */
1809 revprops = apr_hash_copy(scratch_pool, revprops);
1810 svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR,
1811 svn_string_create(sess->username, scratch_pool));
1812
1813 return svn_error_trace(svn_repos__get_commit_ev2(
1814 editor, sess->repos, NULL /* authz */,
1815 NULL /* authz_repos_name */, NULL /* authz_user */,
1816 revprops,
1817 deltify_etc, deb, cancel_func, cancel_baton,
1818 result_pool, scratch_pool));
1819 }
1820
1821 /* Trivially forward repos-layer callbacks to RA-layer callbacks.
1822 * Their signatures are the same. */
1823 typedef struct dirent_receiver_baton_t
1824 {
1825 svn_ra_dirent_receiver_t receiver;
1826 void *receiver_baton;
1827 } dirent_receiver_baton_t;
1828
1829 static svn_error_t *
dirent_receiver(const char * rel_path,svn_dirent_t * dirent,void * baton,apr_pool_t * pool)1830 dirent_receiver(const char *rel_path,
1831 svn_dirent_t *dirent,
1832 void *baton,
1833 apr_pool_t *pool)
1834 {
1835 dirent_receiver_baton_t *b = baton;
1836 return b->receiver(rel_path, dirent, b->receiver_baton, pool);
1837 }
1838
1839 static svn_error_t *
svn_ra_local__list(svn_ra_session_t * session,const char * path,svn_revnum_t revision,const apr_array_header_t * patterns,svn_depth_t depth,apr_uint32_t dirent_fields,svn_ra_dirent_receiver_t receiver,void * receiver_baton,apr_pool_t * pool)1840 svn_ra_local__list(svn_ra_session_t *session,
1841 const char *path,
1842 svn_revnum_t revision,
1843 const apr_array_header_t *patterns,
1844 svn_depth_t depth,
1845 apr_uint32_t dirent_fields,
1846 svn_ra_dirent_receiver_t receiver,
1847 void *receiver_baton,
1848 apr_pool_t *pool)
1849 {
1850 svn_ra_local__session_baton_t *sess = session->priv;
1851 svn_fs_root_t *root;
1852 svn_boolean_t path_info_only = (dirent_fields & ~SVN_DIRENT_KIND) == 0;
1853
1854 dirent_receiver_baton_t baton;
1855 baton.receiver = receiver;
1856 baton.receiver_baton = receiver_baton;
1857
1858 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1859 path = svn_dirent_join(sess->fs_path->data, path, pool);
1860 return svn_error_trace(svn_repos_list(root, path, patterns, depth,
1861 path_info_only, NULL, NULL,
1862 dirent_receiver, &baton,
1863 sess->callbacks
1864 ? sess->callbacks->cancel_func
1865 : NULL,
1866 sess->callback_baton, pool));
1867 }
1868
1869 /*----------------------------------------------------------------*/
1870
1871 static const svn_version_t *
ra_local_version(void)1872 ra_local_version(void)
1873 {
1874 SVN_VERSION_BODY;
1875 }
1876
1877 /** The ra_vtable **/
1878
1879 static const svn_ra__vtable_t ra_local_vtable =
1880 {
1881 ra_local_version,
1882 svn_ra_local__get_description,
1883 svn_ra_local__get_schemes,
1884 svn_ra_local__open,
1885 svn_ra_local__dup_session,
1886 svn_ra_local__reparent,
1887 svn_ra_local__get_session_url,
1888 svn_ra_local__get_latest_revnum,
1889 svn_ra_local__get_dated_revision,
1890 svn_ra_local__change_rev_prop,
1891 svn_ra_local__rev_proplist,
1892 svn_ra_local__rev_prop,
1893 svn_ra_local__get_commit_editor,
1894 svn_ra_local__get_file,
1895 svn_ra_local__get_dir,
1896 svn_ra_local__get_mergeinfo,
1897 svn_ra_local__do_update,
1898 svn_ra_local__do_switch,
1899 svn_ra_local__do_status,
1900 svn_ra_local__do_diff,
1901 svn_ra_local__get_log,
1902 svn_ra_local__do_check_path,
1903 svn_ra_local__stat,
1904 svn_ra_local__get_uuid,
1905 svn_ra_local__get_repos_root,
1906 svn_ra_local__get_locations,
1907 svn_ra_local__get_location_segments,
1908 svn_ra_local__get_file_revs,
1909 svn_ra_local__lock,
1910 svn_ra_local__unlock,
1911 svn_ra_local__get_lock,
1912 svn_ra_local__get_locks,
1913 svn_ra_local__replay,
1914 svn_ra_local__has_capability,
1915 svn_ra_local__replay_range,
1916 svn_ra_local__get_deleted_rev,
1917 svn_ra_local__get_inherited_props,
1918 NULL /* set_svn_ra_open */,
1919 svn_ra_local__list ,
1920 svn_ra_local__register_editor_shim_callbacks,
1921 svn_ra_local__get_commit_ev2,
1922 NULL /* replay_range_ev2 */
1923 };
1924
1925
1926 /*----------------------------------------------------------------*/
1927
1928 /** The One Public Routine, called by libsvn_ra **/
1929
1930 svn_error_t *
svn_ra_local__init(const svn_version_t * loader_version,const svn_ra__vtable_t ** vtable,apr_pool_t * pool)1931 svn_ra_local__init(const svn_version_t *loader_version,
1932 const svn_ra__vtable_t **vtable,
1933 apr_pool_t *pool)
1934 {
1935 static const svn_version_checklist_t checklist[] =
1936 {
1937 { "svn_subr", svn_subr_version },
1938 { "svn_delta", svn_delta_version },
1939 { "svn_repos", svn_repos_version },
1940 { "svn_fs", svn_fs_version },
1941 { NULL, NULL }
1942 };
1943
1944
1945 /* Simplified version check to make sure we can safely use the
1946 VTABLE parameter. The RA loader does a more exhaustive check. */
1947 if (loader_version->major != SVN_VER_MAJOR)
1948 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
1949 _("Unsupported RA loader version (%d) for "
1950 "ra_local"),
1951 loader_version->major);
1952
1953 SVN_ERR(svn_ver_check_list2(ra_local_version(), checklist, svn_ver_equal));
1954
1955 #ifndef SVN_LIBSVN_RA_LINKS_RA_LOCAL
1956 /* This means the library was loaded as a DSO, so use the DSO pool. */
1957 SVN_ERR(svn_fs_initialize(svn_dso__pool()));
1958 #endif
1959
1960 *vtable = &ra_local_vtable;
1961
1962 return SVN_NO_ERROR;
1963 }
1964
1965 /* Compatibility wrapper for the 1.1 and before API. */
1966 #define NAME "ra_local"
1967 #define DESCRIPTION RA_LOCAL_DESCRIPTION
1968 #define VTBL ra_local_vtable
1969 #define INITFUNC svn_ra_local__init
1970 #define COMPAT_INITFUNC svn_ra_local_init
1971 #include "../libsvn_ra/wrapper_template.h"
1972