2
* ra_plugin.c : the main RA module for local repository access
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
22
#include "svn_delta.h"
23
#include "svn_repos.h"
24
#include "svn_pools.h"
26
#include "svn_props.h"
28
#include "svn_private_config.h"
29
#include "../libsvn_ra/ra_loader.h"
31
#define APR_WANT_STRFUNC
36
/*----------------------------------------------------------------*/
39
/* The reporter vtable needed by do_update() */
40
typedef struct reporter_baton_t
42
svn_ra_local__session_baton_t *session;
49
make_reporter_baton (svn_ra_local__session_baton_t *session,
53
reporter_baton_t *rbaton = apr_palloc (pool, sizeof (*rbaton));
54
rbaton->session = session;
55
rbaton->report_baton = report_baton;
61
reporter_set_path (void *reporter_baton,
63
svn_revnum_t revision,
64
svn_boolean_t start_empty,
65
const char *lock_token,
68
reporter_baton_t *rbaton = reporter_baton;
69
return svn_repos_set_path2 (rbaton->report_baton, path,
70
revision, start_empty, lock_token, pool);
75
reporter_delete_path (void *reporter_baton,
79
reporter_baton_t *rbaton = reporter_baton;
80
return svn_repos_delete_path (rbaton->report_baton, path, pool);
85
reporter_link_path (void *reporter_baton,
88
svn_revnum_t revision,
89
svn_boolean_t start_empty,
90
const char *lock_token,
93
reporter_baton_t *rbaton = reporter_baton;
94
const char *fs_path = NULL;
95
const char *repos_url_decoded;
98
url = svn_path_uri_decode(url, pool);
99
repos_url_decoded = svn_path_uri_decode(rbaton->session->repos_url, pool);
100
repos_url_len = strlen(repos_url_decoded);
101
if (strncmp(url, repos_url_decoded, repos_url_len) != 0)
102
return svn_error_createf (SVN_ERR_RA_ILLEGAL_URL, NULL,
104
"is not the same repository as\n"
105
"'%s'"), url, rbaton->session->repos_url);
106
fs_path = url + repos_url_len;
108
return svn_repos_link_path2 (rbaton->report_baton, path, fs_path, revision,
109
start_empty, lock_token, pool);
114
reporter_finish_report (void *reporter_baton,
117
reporter_baton_t *rbaton = reporter_baton;
118
return svn_repos_finish_report (rbaton->report_baton, pool);
123
reporter_abort_report (void *reporter_baton,
126
reporter_baton_t *rbaton = reporter_baton;
127
return svn_repos_abort_report (rbaton->report_baton, pool);
131
static const svn_ra_reporter2_t ra_local_reporter =
134
reporter_delete_path,
136
reporter_finish_report,
137
reporter_abort_report
141
svn_ra_local__get_file_revs (svn_ra_session_t *session,
145
svn_ra_file_rev_handler_t handler,
149
svn_ra_local__session_baton_t *sbaton = session->priv;
150
const char *abs_path = sbaton->fs_path;
152
/* Concatenate paths */
153
abs_path = svn_path_join (abs_path, path, pool);
155
return svn_repos_get_file_revs (sbaton->repos, abs_path, start, end, NULL,
156
NULL, handler, handler_baton, pool);
159
/* Pool cleanup handler: Ensure that the access descriptor of the filesystem
160
DATA is set to NULL. */
162
cleanup_access (void *data)
167
serr = svn_fs_set_access (fs, NULL);
171
apr_status_t apr_err = serr->apr_err;
172
svn_error_clear(serr);
180
get_username (svn_ra_session_t *session,
183
svn_ra_local__session_baton_t *baton = session->priv;
184
svn_auth_iterstate_t *iterstate;
185
svn_fs_access_t *access_ctx;
187
/* If we've already found the username don't ask for it again. */
188
if (! baton->username)
190
/* Get a username somehow, so we have some svn:author property to
191
attach to a commit. */
192
if (baton->callbacks->auth_baton)
195
svn_auth_cred_username_t *username_creds;
196
SVN_ERR (svn_auth_first_credentials (&creds, &iterstate,
197
SVN_AUTH_CRED_USERNAME,
198
baton->uuid, /* realmstring */
199
baton->callbacks->auth_baton,
202
/* No point in calling next_creds(), since that assumes that the
203
first_creds() somehow failed to authenticate. But there's no
204
challenge going on, so we use whatever creds we get back on
206
username_creds = creds;
207
if (username_creds && username_creds->username)
209
baton->username = apr_pstrdup (session->pool,
210
username_creds->username);
211
svn_error_clear (svn_auth_save_credentials (iterstate, pool));
214
baton->username = "";
217
baton->username = "";
220
/* If we have a real username, attach it to the filesystem so that it can
221
be used to validate locks. Even if there already is a user context
222
associated, it may contain irrelevant lock tokens, so always create a new.
224
if (*baton->username)
226
SVN_ERR (svn_fs_create_access (&access_ctx, baton->username,
228
SVN_ERR (svn_fs_set_access (baton->fs, access_ctx));
230
/* Make sure this context is disassociated when the pool gets
232
apr_pool_cleanup_register (pool, baton->fs, cleanup_access,
233
apr_pool_cleanup_null);
240
/*----------------------------------------------------------------*/
242
/** The RA vtable routines **/
244
#define RA_LOCAL_DESCRIPTION \
245
N_("Module for accessing a repository on local disk.")
248
svn_ra_local__get_description (void)
250
return _(RA_LOCAL_DESCRIPTION);
253
static const char * const *
254
svn_ra_local__get_schemes (apr_pool_t *pool)
256
static const char *schemes[] = { "file", NULL };
262
svn_ra_local__open (svn_ra_session_t *session,
263
const char *repos_URL,
264
const svn_ra_callbacks_t *callbacks,
265
void *callback_baton,
269
svn_ra_local__session_baton_t *baton;
271
/* Allocate and stash the session_baton args we have already. */
272
baton = apr_pcalloc (pool, sizeof(*baton));
273
baton->repository_URL = apr_pstrdup (session->pool, repos_URL);
274
baton->callbacks = callbacks;
275
baton->callback_baton = callback_baton;
277
/* Look through the URL, figure out which part points to the
278
repository, and which part is the path *within* the
280
SVN_ERR_W (svn_ra_local__split_URL (&(baton->repos),
283
baton->repository_URL,
285
_("Unable to open an ra_local session to URL"));
287
/* Cache the filesystem object from the repos here for
289
baton->fs = svn_repos_fs (baton->repos);
291
/* Cache the repository UUID as well */
292
SVN_ERR (svn_fs_get_uuid (baton->fs, &baton->uuid, session->pool));
294
/* Be sure username is NULL so we know to look it up / ask for it */
295
baton->username = NULL;
297
session->priv = baton;
304
svn_ra_local__get_latest_revnum (svn_ra_session_t *session,
305
svn_revnum_t *latest_revnum,
308
svn_ra_local__session_baton_t *baton = session->priv;
310
SVN_ERR (svn_fs_youngest_rev (latest_revnum, baton->fs, pool));
318
svn_ra_local__get_dated_revision (svn_ra_session_t *session,
319
svn_revnum_t *revision,
323
svn_ra_local__session_baton_t *baton = session->priv;
325
SVN_ERR (svn_repos_dated_revision (revision, baton->repos, tm, pool));
332
svn_ra_local__change_rev_prop (svn_ra_session_t *session,
335
const svn_string_t *value,
338
svn_ra_local__session_baton_t *baton = session->priv;
340
SVN_ERR (get_username (session, pool));
342
SVN_ERR (svn_repos_fs_change_rev_prop2 (baton->repos, rev, baton->username,
343
name, value, NULL, NULL, pool));
350
svn_ra_local__get_uuid (svn_ra_session_t *session,
354
svn_ra_local__session_baton_t *baton = session->priv;
362
svn_ra_local__get_repos_root (svn_ra_session_t *session,
366
svn_ra_local__session_baton_t *baton = session->priv;
368
*url = baton->repos_url;
374
svn_ra_local__rev_proplist (svn_ra_session_t *session,
379
svn_ra_local__session_baton_t *baton = session->priv;
381
SVN_ERR (svn_repos_fs_revision_proplist (props, baton->repos, rev,
389
svn_ra_local__rev_prop (svn_ra_session_t *session,
392
svn_string_t **value,
395
svn_ra_local__session_baton_t *baton = session->priv;
397
SVN_ERR (svn_repos_fs_revision_prop (value, baton->repos, rev, name,
404
struct deltify_etc_baton
406
svn_fs_t *fs; /* the fs to deltify in */
407
svn_repos_t *repos; /* repos for unlocking */
408
const char *fs_path; /* fs-path part of split session URL */
409
apr_hash_t *lock_tokens; /* tokens to unlock, if any */
410
apr_pool_t *pool; /* pool for scratch work */
411
svn_commit_callback_t callback; /* the original callback */
412
void *callback_baton; /* the original callback's baton */
415
/* This implements 'svn_commit_callback_t'. Its invokes the original
416
(wrapped) callback, but also does deltification on the new revision and
417
possibly unlocks committed paths.
418
BATON is 'struct deltify_etc_baton *'. */
420
deltify_etc (svn_revnum_t new_revision,
425
struct deltify_etc_baton *db = baton;
426
svn_error_t *err1, *err2;
427
apr_hash_index_t *hi;
428
apr_pool_t *iterpool;
430
/* Invoke the original callback first, in case someone's waiting to
431
know the revision number so they can go off and annotate an
432
issue or something. */
433
err1 = (*db->callback) (new_revision, date, author, db->callback_baton);
435
/* Maybe unlock the paths. */
438
iterpool = svn_pool_create (db->pool);
439
for (hi = apr_hash_first (db->pool, db->lock_tokens); hi;
440
hi = apr_hash_next (hi))
442
const void *rel_path;
444
const char *abs_path, *token;
446
svn_pool_clear (iterpool);
447
apr_hash_this (hi, &rel_path, NULL, &val);
449
abs_path = svn_path_join (db->fs_path, rel_path, iterpool);
450
/* We may get errors here if the lock was broken or stolen
451
after the commit succeeded. This is fine and should be
453
svn_error_clear (svn_repos_fs_unlock (db->repos, abs_path, token,
456
svn_pool_destroy (iterpool);
459
/* But, deltification shouldn't be stopped just because someone's
460
random callback failed, so proceed unconditionally on to
462
err2 = svn_fs_deltify_revision (db->fs, new_revision, db->pool);
464
/* It's more interesting if the original callback failed, so let
465
that one dominate. */
468
svn_error_clear (err2);
477
svn_ra_local__get_commit_editor (svn_ra_session_t *session,
478
const svn_delta_editor_t **editor,
481
svn_commit_callback_t callback,
482
void *callback_baton,
483
apr_hash_t *lock_tokens,
484
svn_boolean_t keep_locks,
487
svn_ra_local__session_baton_t *sess_baton = session->priv;
488
struct deltify_etc_baton *db = apr_palloc (pool, sizeof(*db));
489
apr_hash_index_t *hi;
490
svn_fs_access_t *fs_access;
492
db->fs = sess_baton->fs;
493
db->repos = sess_baton->repos;
494
db->fs_path = sess_baton->fs_path;
496
db->lock_tokens = lock_tokens;
498
db->lock_tokens = NULL;
500
db->callback = callback;
501
db->callback_baton = callback_baton;
503
SVN_ERR (get_username (session, pool));
505
/* If there are lock tokens to add, do so. */
508
SVN_ERR (svn_fs_get_access (&fs_access, sess_baton->fs));
510
/* If there is no access context, the filesystem will scream if a
514
for (hi = apr_hash_first (pool, lock_tokens); hi;
515
hi = apr_hash_next (hi))
520
apr_hash_this (hi, NULL, NULL, &val);
522
SVN_ERR (svn_fs_access_add_lock_token (fs_access, token));
527
/* Get the repos commit-editor */
528
SVN_ERR (svn_repos_get_commit_editor2
529
(editor, edit_baton, sess_baton->repos, NULL,
530
svn_path_uri_decode (sess_baton->repos_url, pool),
532
sess_baton->username, log_msg,
533
deltify_etc, db, pool));
540
make_reporter (svn_ra_session_t *session,
541
const svn_ra_reporter2_t **reporter,
543
svn_revnum_t revision,
545
const char *other_url,
546
svn_boolean_t text_deltas,
547
svn_boolean_t recurse,
548
svn_boolean_t ignore_ancestry,
549
const svn_delta_editor_t *editor,
553
svn_ra_local__session_baton_t *sbaton = session->priv;
556
const char *other_fs_path = NULL;
557
const char *repos_url_decoded;
559
/* Get the HEAD revision if one is not supplied. */
560
if (! SVN_IS_VALID_REVNUM(revision))
561
SVN_ERR (svn_ra_local__get_latest_revnum (session, &revision, pool));
563
/* If OTHER_URL was provided, validate it and convert it into a
564
regular filesystem path. */
567
other_url = svn_path_uri_decode (other_url, pool);
568
repos_url_decoded = svn_path_uri_decode (sbaton->repos_url, pool);
569
repos_url_len = strlen(repos_url_decoded);
571
/* Sanity check: the other_url better be in the same repository as
572
the original session url! */
573
if (strncmp (other_url, repos_url_decoded, repos_url_len) != 0)
574
return svn_error_createf
575
(SVN_ERR_RA_ILLEGAL_URL, NULL,
577
"is not the same repository as\n"
578
"'%s'"), other_url, sbaton->repos_url);
580
other_fs_path = other_url + repos_url_len;
583
/* Pass back our reporter */
584
*reporter = &ra_local_reporter;
586
SVN_ERR (get_username (session, pool));
588
/* Build a reporter baton. */
589
SVN_ERR (svn_repos_begin_report (&rbaton,
605
/* Wrap the report baton given us by the repos layer with our own
607
*report_baton = make_reporter_baton (sbaton, rbaton, pool);
614
svn_ra_local__do_update (svn_ra_session_t *session,
615
const svn_ra_reporter2_t **reporter,
617
svn_revnum_t update_revision,
618
const char *update_target,
619
svn_boolean_t recurse,
620
const svn_delta_editor_t *update_editor,
624
return make_reporter (session,
640
svn_ra_local__do_switch (svn_ra_session_t *session,
641
const svn_ra_reporter2_t **reporter,
643
svn_revnum_t update_revision,
644
const char *update_target,
645
svn_boolean_t recurse,
646
const char *switch_url,
647
const svn_delta_editor_t *update_editor,
651
return make_reporter (session,
667
svn_ra_local__do_status (svn_ra_session_t *session,
668
const svn_ra_reporter2_t **reporter,
670
const char *status_target,
671
svn_revnum_t revision,
672
svn_boolean_t recurse,
673
const svn_delta_editor_t *status_editor,
677
return make_reporter (session,
693
svn_ra_local__do_diff (svn_ra_session_t *session,
694
const svn_ra_reporter2_t **reporter,
696
svn_revnum_t update_revision,
697
const char *update_target,
698
svn_boolean_t recurse,
699
svn_boolean_t ignore_ancestry,
700
const char *switch_url,
701
const svn_delta_editor_t *update_editor,
705
return make_reporter (session,
721
svn_ra_local__get_log (svn_ra_session_t *session,
722
const apr_array_header_t *paths,
726
svn_boolean_t discover_changed_paths,
727
svn_boolean_t strict_node_history,
728
svn_log_message_receiver_t receiver,
729
void *receiver_baton,
732
svn_ra_local__session_baton_t *sbaton = session->priv;
734
apr_array_header_t *abs_paths =
735
apr_array_make (pool, 0, sizeof (const char *));
739
for (i = 0; i < paths->nelts; i++)
741
const char *abs_path = "";
742
const char *relative_path = (((const char **)(paths)->elts)[i]);
744
/* Append the relative paths to the base FS path to get an
745
absolute repository path. */
746
abs_path = svn_path_join (sbaton->fs_path, relative_path, pool);
747
(*((const char **)(apr_array_push (abs_paths)))) = abs_path;
751
return svn_repos_get_logs3 (sbaton->repos,
756
discover_changed_paths,
766
svn_ra_local__do_check_path (svn_ra_session_t *session,
768
svn_revnum_t revision,
769
svn_node_kind_t *kind,
772
svn_ra_local__session_baton_t *sbaton = session->priv;
774
const char *abs_path = sbaton->fs_path;
776
/* ### Not sure if this counts as a workaround or not. The
777
session baton uses the empty string to mean root, and not
778
sure that should change. However, it would be better to use
779
a path library function to add this separator -- hardcoding
780
it is totally bogus. See issue #559, though it may be only
781
tangentially related. */
782
if (abs_path[0] == '\0')
785
/* If we were given a relative path to append, append it. */
787
abs_path = svn_path_join (abs_path, path, pool);
789
if (! SVN_IS_VALID_REVNUM (revision))
790
SVN_ERR (svn_fs_youngest_rev (&revision, sbaton->fs, pool));
791
SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, revision, pool));
792
SVN_ERR (svn_fs_check_path (kind, root, abs_path, pool));
798
svn_ra_local__stat (svn_ra_session_t *session,
800
svn_revnum_t revision,
801
svn_dirent_t **dirent,
804
svn_ra_local__session_baton_t *sbaton = session->priv;
806
const char *abs_path = sbaton->fs_path;
808
/* ### see note above in __do_check_path() */
809
if (abs_path[0] == '\0')
813
abs_path = svn_path_join (abs_path, path, pool);
815
if (! SVN_IS_VALID_REVNUM (revision))
816
SVN_ERR (svn_fs_youngest_rev (&revision, sbaton->fs, pool));
817
SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, revision, pool));
819
SVN_ERR (svn_repos_stat (dirent, root, abs_path, pool));
828
get_node_props (apr_hash_t **props,
829
svn_ra_local__session_baton_t *sbaton,
834
svn_revnum_t cmt_rev;
835
const char *cmt_date, *cmt_author;
837
/* Create a hash with props attached to the fs node. */
838
SVN_ERR (svn_fs_node_proplist (props, root, path, pool));
840
/* Now add some non-tweakable metadata to the hash as well... */
842
/* The so-called 'entryprops' with info about CR & friends. */
843
SVN_ERR (svn_repos_get_committed_info (&cmt_rev, &cmt_date,
844
&cmt_author, root, path, pool));
846
apr_hash_set (*props,
847
SVN_PROP_ENTRY_COMMITTED_REV,
849
svn_string_createf (pool, "%ld", cmt_rev));
850
apr_hash_set (*props,
851
SVN_PROP_ENTRY_COMMITTED_DATE,
853
cmt_date ? svn_string_create (cmt_date, pool) : NULL);
854
apr_hash_set (*props,
855
SVN_PROP_ENTRY_LAST_AUTHOR,
857
cmt_author ? svn_string_create (cmt_author, pool) : NULL);
858
apr_hash_set (*props,
861
svn_string_create (sbaton->uuid, pool));
863
/* We have no 'wcprops' in ra_local, but might someday. */
869
/* Getting just one file. */
871
svn_ra_local__get_file (svn_ra_session_t *session,
873
svn_revnum_t revision,
874
svn_stream_t *stream,
875
svn_revnum_t *fetched_rev,
880
svn_stream_t *contents;
881
svn_revnum_t youngest_rev;
882
svn_ra_local__session_baton_t *sbaton = session->priv;
883
const char *abs_path = sbaton->fs_path;
885
/* ### Not sure if this counts as a workaround or not. The
886
session baton uses the empty string to mean root, and not
887
sure that should change. However, it would be better to use
888
a path library function to add this separator -- hardcoding
889
it is totally bogus. See issue #559, though it may be only
890
tangentially related. */
891
if (abs_path[0] == '\0')
894
/* If we were given a relative path to append, append it. */
896
abs_path = svn_path_join (abs_path, path, pool);
898
/* Open the revision's root. */
899
if (! SVN_IS_VALID_REVNUM (revision))
901
SVN_ERR (svn_fs_youngest_rev (&youngest_rev, sbaton->fs, pool));
902
SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, youngest_rev, pool));
903
if (fetched_rev != NULL)
904
*fetched_rev = youngest_rev;
907
SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, revision, pool));
911
/* Get a stream representing the file's contents. */
912
SVN_ERR (svn_fs_file_contents (&contents, root, abs_path, pool));
914
/* Now push data from the fs stream back at the caller's stream.
915
Note that this particular RA layer does not computing a
916
checksum as we go, and confirming it against the repository's
917
checksum when done. That's because it calls
918
svn_fs_file_contents() directly, which already checks the
919
stored checksum, and all we're doing here is writing bytes in
920
a loop. Truly, Nothing Can Go Wrong :-). But RA layers that
921
go over a network should confirm the checksum. */
922
SVN_ERR (svn_stream_copy (contents, stream, pool));
923
SVN_ERR (svn_stream_close (contents));
926
/* Handle props if requested. */
928
SVN_ERR (get_node_props (props, sbaton, root, abs_path, pool));
935
/* Getting a directory's entries */
937
svn_ra_local__get_dir (svn_ra_session_t *session,
939
svn_revnum_t revision,
940
apr_hash_t **dirents,
941
svn_revnum_t *fetched_rev,
946
svn_revnum_t youngest_rev;
948
apr_hash_index_t *hi;
949
svn_ra_local__session_baton_t *sbaton = session->priv;
950
const char *abs_path = sbaton->fs_path;
953
/* ### Not sure if this counts as a workaround or not. The
954
session baton uses the empty string to mean root, and not
955
sure that should change. However, it would be better to use
956
a path library function to add this separator -- hardcoding
957
it is totally bogus. See issue #559, though it may be only
958
tangentially related. */
959
if (abs_path[0] == '\0')
962
/* If we were given a relative path to append, append it. */
964
abs_path = svn_path_join (abs_path, path, pool);
966
/* Open the revision's root. */
967
if (! SVN_IS_VALID_REVNUM (revision))
969
SVN_ERR (svn_fs_youngest_rev (&youngest_rev, sbaton->fs, pool));
970
SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, youngest_rev, pool));
971
if (fetched_rev != NULL)
972
*fetched_rev = youngest_rev;
975
SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, revision, pool));
979
/* Get the dir's entries. */
980
SVN_ERR (svn_fs_dir_entries (&entries, root, abs_path, pool));
982
/* Loop over the fs dirents, and build a hash of general
984
*dirents = apr_hash_make (pool);
985
subpool = svn_pool_create (pool);
986
for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
990
apr_hash_t *prophash;
991
const char *datestring, *entryname, *fullpath;
992
svn_fs_dirent_t *fs_entry;
993
svn_dirent_t *entry = apr_pcalloc (pool, sizeof(*entry));
995
svn_pool_clear (subpool);
997
apr_hash_this (hi, &key, NULL, &val);
998
entryname = (const char *) key;
999
fs_entry = (svn_fs_dirent_t *) val;
1002
fullpath = svn_path_join (abs_path, entryname, subpool);
1003
entry->kind = fs_entry->kind;
1006
if (entry->kind == svn_node_dir)
1009
SVN_ERR (svn_fs_file_length (&(entry->size), root,
1010
fullpath, subpool));
1013
SVN_ERR (svn_fs_node_proplist (&prophash, root, fullpath, subpool));
1014
entry->has_props = (apr_hash_count (prophash)) ? TRUE : FALSE;
1016
/* created_rev & friends */
1017
SVN_ERR (svn_repos_get_committed_info (&(entry->created_rev),
1019
&(entry->last_author),
1020
root, fullpath, subpool));
1022
SVN_ERR (svn_time_from_cstring (&(entry->time), datestring, pool));
1023
if (entry->last_author)
1024
entry->last_author = apr_pstrdup (pool, entry->last_author);
1027
apr_hash_set (*dirents, entryname, APR_HASH_KEY_STRING, entry);
1029
svn_pool_destroy (subpool);
1032
/* Handle props if requested. */
1034
SVN_ERR (get_node_props (props, sbaton, root, abs_path, pool));
1036
return SVN_NO_ERROR;
1039
static svn_error_t *
1040
svn_ra_local__get_locations (svn_ra_session_t *session,
1041
apr_hash_t **locations,
1042
const char *relative_path,
1043
svn_revnum_t peg_revision,
1044
apr_array_header_t *location_revisions,
1047
svn_ra_local__session_baton_t *sbaton = session->priv;
1048
const char *abs_path;
1050
/* Append the relative path to the base FS path to get an
1051
absolute repository path. */
1052
abs_path = svn_path_join (sbaton->fs_path, relative_path, pool);
1054
SVN_ERR (svn_repos_trace_node_locations (sbaton->fs, locations, abs_path,
1055
peg_revision, location_revisions,
1058
return SVN_NO_ERROR;
1063
static svn_error_t *
1064
svn_ra_local__lock (svn_ra_session_t *session,
1065
apr_hash_t *path_revs,
1066
const char *comment,
1067
svn_boolean_t force,
1068
svn_ra_lock_callback_t lock_func,
1072
svn_ra_local__session_baton_t *sess = session->priv;
1073
apr_hash_index_t *hi;
1074
apr_pool_t *iterpool = svn_pool_create (pool);
1076
/* A username is absolutely required to lock a path. */
1077
SVN_ERR (get_username (session, pool));
1079
for (hi = apr_hash_first (pool, path_revs); hi; hi = apr_hash_next (hi))
1085
svn_revnum_t *revnum;
1086
const char *abs_path;
1087
svn_error_t *err, *callback_err = NULL;
1089
svn_pool_clear (iterpool);
1090
apr_hash_this (hi, &key, NULL, &val);
1094
abs_path = svn_path_join (sess->fs_path, path, iterpool);
1096
/* This wrapper will call pre- and post-lock hooks. */
1097
err = svn_repos_fs_lock (&lock, sess->repos, abs_path, NULL, comment, 0,
1098
0 /* no expiration */, *revnum, force,
1101
if (err && !SVN_ERR_IS_LOCK_ERROR (err))
1105
callback_err = lock_func (lock_baton, path, TRUE, err ? NULL : lock,
1108
svn_error_clear (err);
1111
return callback_err;
1114
svn_pool_destroy (iterpool);
1116
return SVN_NO_ERROR;
1120
static svn_error_t *
1121
svn_ra_local__unlock (svn_ra_session_t *session,
1122
apr_hash_t *path_tokens,
1123
svn_boolean_t force,
1124
svn_ra_lock_callback_t lock_func,
1128
svn_ra_local__session_baton_t *sess = session->priv;
1129
apr_hash_index_t *hi;
1130
apr_pool_t *iterpool = svn_pool_create (pool);
1132
/* A username is absolutely required to unlock a path. */
1133
SVN_ERR (get_username (session, pool));
1135
for (hi = apr_hash_first (pool, path_tokens); hi; hi = apr_hash_next (hi))
1140
const char *abs_path, *token;
1141
svn_error_t *err, *callback_err = NULL;
1143
svn_pool_clear (iterpool);
1144
apr_hash_this (hi, &key, NULL, &val);
1146
/* Since we can't store NULL values in a hash, we turn "" to
1148
if (strcmp (val, "") != 0)
1153
abs_path = svn_path_join (sess->fs_path, path, iterpool);
1155
/* This wrapper will call pre- and post-unlock hooks. */
1156
err = svn_repos_fs_unlock (sess->repos, abs_path, token, force,
1159
if (err && !SVN_ERR_IS_UNLOCK_ERROR (err))
1163
callback_err = lock_func (lock_baton, path, FALSE, NULL, err, iterpool);
1165
svn_error_clear (err);
1168
return callback_err;
1171
svn_pool_destroy (iterpool);
1173
return SVN_NO_ERROR;
1178
static svn_error_t *
1179
svn_ra_local__get_lock (svn_ra_session_t *session,
1184
svn_ra_local__session_baton_t *sess = session->priv;
1185
const char *abs_path;
1187
/* Get the absolute path. */
1188
abs_path = svn_path_join (sess->fs_path, path, pool);
1190
SVN_ERR (svn_fs_get_lock (lock, sess->fs, abs_path, pool));
1192
return SVN_NO_ERROR;
1197
static svn_error_t *
1198
svn_ra_local__get_locks (svn_ra_session_t *session,
1203
svn_ra_local__session_baton_t *sess = session->priv;
1204
const char *abs_path;
1206
/* Get the absolute path. */
1207
abs_path = svn_path_join (sess->fs_path, path, pool);
1209
/* Kinda silly to call the repos wrapper, since we have no authz
1210
func to give it. But heck, why not. */
1211
SVN_ERR (svn_repos_fs_get_locks (locks, sess->repos, abs_path,
1214
return SVN_NO_ERROR;
1220
/*----------------------------------------------------------------*/
1222
static const svn_version_t *
1223
ra_local_version (void)
1228
/** The ra_vtable **/
1230
static const svn_ra__vtable_t ra_local_vtable =
1233
svn_ra_local__get_description,
1234
svn_ra_local__get_schemes,
1236
svn_ra_local__get_latest_revnum,
1237
svn_ra_local__get_dated_revision,
1238
svn_ra_local__change_rev_prop,
1239
svn_ra_local__rev_proplist,
1240
svn_ra_local__rev_prop,
1241
svn_ra_local__get_commit_editor,
1242
svn_ra_local__get_file,
1243
svn_ra_local__get_dir,
1244
svn_ra_local__do_update,
1245
svn_ra_local__do_switch,
1246
svn_ra_local__do_status,
1247
svn_ra_local__do_diff,
1248
svn_ra_local__get_log,
1249
svn_ra_local__do_check_path,
1251
svn_ra_local__get_uuid,
1252
svn_ra_local__get_repos_root,
1253
svn_ra_local__get_locations,
1254
svn_ra_local__get_file_revs,
1256
svn_ra_local__unlock,
1257
svn_ra_local__get_lock,
1258
svn_ra_local__get_locks,
1262
/*----------------------------------------------------------------*/
1264
/** The One Public Routine, called by libsvn_ra **/
1267
svn_ra_local__init (const svn_version_t *loader_version,
1268
const svn_ra__vtable_t **vtable,
1271
static const svn_version_checklist_t checklist[] =
1273
{ "svn_subr", svn_subr_version },
1274
{ "svn_delta", svn_delta_version },
1275
{ "svn_repos", svn_repos_version },
1276
{ "svn_fs", svn_fs_version },
1281
/* Simplified version check to make sure we can safely use the
1282
VTABLE parameter. The RA loader does a more exhaustive check. */
1283
if (loader_version->major != SVN_VER_MAJOR)
1284
return svn_error_createf (SVN_ERR_VERSION_MISMATCH, NULL,
1285
_("Unsupported RA loader version (%d) for "
1287
loader_version->major);
1289
SVN_ERR (svn_ver_check_list (ra_local_version(), checklist));
1291
#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
1292
/* This assumes that POOL was the pool used to load the dso. */
1293
SVN_ERR (svn_fs_initialize (pool));
1296
*vtable = &ra_local_vtable;
1298
return SVN_NO_ERROR;
1301
/* Compatibility wrapper for the 1.1 and before API. */
1302
#define NAME "ra_local"
1303
#define DESCRIPTION RA_LOCAL_DESCRIPTION
1304
#define VTBL ra_local_vtable
1305
#define INITFUNC svn_ra_local__init
1306
#define COMPAT_INITFUNC svn_ra_local_init
1307
#include "../libsvn_ra/wrapper_template.h"