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