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