1251881Speter/* fs-wrap.c --- filesystem interface wrappers.
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter#include <stdio.h>
24251881Speter#include <string.h>
25251881Speter#include <ctype.h>
26251881Speter
27251881Speter#include "svn_hash.h"
28251881Speter#include "svn_pools.h"
29251881Speter#include "svn_error.h"
30251881Speter#include "svn_fs.h"
31251881Speter#include "svn_path.h"
32251881Speter#include "svn_props.h"
33251881Speter#include "svn_repos.h"
34251881Speter#include "svn_time.h"
35251881Speter#include "svn_sorts.h"
36251881Speter#include "repos.h"
37251881Speter#include "svn_private_config.h"
38251881Speter#include "private/svn_repos_private.h"
39251881Speter#include "private/svn_utf_private.h"
40251881Speter#include "private/svn_fspath.h"
41251881Speter
42251881Speter
43251881Speter/*** Commit wrappers ***/
44251881Speter
45251881Spetersvn_error_t *
46251881Spetersvn_repos_fs_commit_txn(const char **conflict_p,
47251881Speter                        svn_repos_t *repos,
48251881Speter                        svn_revnum_t *new_rev,
49251881Speter                        svn_fs_txn_t *txn,
50251881Speter                        apr_pool_t *pool)
51251881Speter{
52251881Speter  svn_error_t *err, *err2;
53251881Speter  const char *txn_name;
54251881Speter  apr_hash_t *props;
55251881Speter  apr_pool_t *iterpool;
56251881Speter  apr_hash_index_t *hi;
57251881Speter  apr_hash_t *hooks_env;
58251881Speter
59251881Speter  *new_rev = SVN_INVALID_REVNUM;
60251881Speter
61251881Speter  /* Parse the hooks-env file (if any). */
62251881Speter  SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
63251881Speter                                     pool, pool));
64251881Speter
65251881Speter  /* Run pre-commit hooks. */
66251881Speter  SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
67251881Speter  SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool));
68251881Speter
69251881Speter  /* Remove any ephemeral transaction properties. */
70251881Speter  SVN_ERR(svn_fs_txn_proplist(&props, txn, pool));
71251881Speter  iterpool = svn_pool_create(pool);
72251881Speter  for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
73251881Speter    {
74251881Speter      const void *key;
75251881Speter      apr_hash_this(hi, &key, NULL, NULL);
76251881Speter
77251881Speter      svn_pool_clear(iterpool);
78251881Speter
79251881Speter      if (strncmp(key, SVN_PROP_TXN_PREFIX,
80251881Speter                  (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
81251881Speter        {
82251881Speter          SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool));
83251881Speter        }
84251881Speter    }
85251881Speter  svn_pool_destroy(iterpool);
86251881Speter
87251881Speter  /* Commit. */
88251881Speter  err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool);
89251881Speter  if (! SVN_IS_VALID_REVNUM(*new_rev))
90251881Speter    return err;
91251881Speter
92251881Speter  /* Run post-commit hooks. */
93251881Speter  if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env,
94251881Speter                                           *new_rev, txn_name, pool)))
95251881Speter    {
96251881Speter      err2 = svn_error_create
97251881Speter               (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2,
98251881Speter                _("Commit succeeded, but post-commit hook failed"));
99251881Speter    }
100251881Speter
101251881Speter  return svn_error_compose_create(err, err2);
102251881Speter}
103251881Speter
104251881Speter
105251881Speter
106251881Speter/*** Transaction creation wrappers. ***/
107251881Speter
108251881Speter
109251881Spetersvn_error_t *
110251881Spetersvn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
111251881Speter                                   svn_repos_t *repos,
112251881Speter                                   svn_revnum_t rev,
113251881Speter                                   apr_hash_t *revprop_table,
114251881Speter                                   apr_pool_t *pool)
115251881Speter{
116251881Speter  apr_array_header_t *revprops;
117251881Speter  const char *txn_name;
118251881Speter  svn_string_t *author = svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR);
119251881Speter  apr_hash_t *hooks_env;
120251881Speter
121251881Speter  /* Parse the hooks-env file (if any). */
122251881Speter  SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
123251881Speter                                     pool, pool));
124251881Speter
125251881Speter  /* Begin the transaction, ask for the fs to do on-the-fly lock checks.
126251881Speter     We fetch its name, too, so the start-commit hook can use it.  */
127251881Speter  SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev,
128251881Speter                            SVN_FS_TXN_CHECK_LOCKS, pool));
129251881Speter  SVN_ERR(svn_fs_txn_name(&txn_name, *txn_p, pool));
130251881Speter
131251881Speter  /* We pass the revision properties to the filesystem by adding them
132251881Speter     as properties on the txn.  Later, when we commit the txn, these
133251881Speter     properties will be copied into the newly created revision. */
134251881Speter  revprops = svn_prop_hash_to_array(revprop_table, pool);
135251881Speter  SVN_ERR(svn_repos_fs_change_txn_props(*txn_p, revprops, pool));
136251881Speter
137251881Speter  /* Run start-commit hooks. */
138251881Speter  SVN_ERR(svn_repos__hooks_start_commit(repos, hooks_env,
139251881Speter                                        author ? author->data : NULL,
140251881Speter                                        repos->client_capabilities, txn_name,
141251881Speter                                        pool));
142251881Speter  return SVN_NO_ERROR;
143251881Speter}
144251881Speter
145251881Speter
146251881Spetersvn_error_t *
147251881Spetersvn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p,
148251881Speter                                  svn_repos_t *repos,
149251881Speter                                  svn_revnum_t rev,
150251881Speter                                  const char *author,
151251881Speter                                  const char *log_msg,
152251881Speter                                  apr_pool_t *pool)
153251881Speter{
154251881Speter  apr_hash_t *revprop_table = apr_hash_make(pool);
155251881Speter  if (author)
156251881Speter    svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
157251881Speter                  svn_string_create(author, pool));
158251881Speter  if (log_msg)
159251881Speter    svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
160251881Speter                  svn_string_create(log_msg, pool));
161251881Speter  return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, revprop_table,
162251881Speter                                            pool);
163251881Speter}
164251881Speter
165251881Speter
166251881Speter/*** Property wrappers ***/
167251881Speter
168251881Spetersvn_error_t *
169251881Spetersvn_repos__validate_prop(const char *name,
170251881Speter                         const svn_string_t *value,
171251881Speter                         apr_pool_t *pool)
172251881Speter{
173251881Speter  svn_prop_kind_t kind = svn_property_kind2(name);
174251881Speter
175253734Speter  /* Allow deleting any property, even a property we don't allow to set. */
176253734Speter  if (value == NULL)
177253734Speter    return SVN_NO_ERROR;
178253734Speter
179251881Speter  /* Disallow setting non-regular properties. */
180251881Speter  if (kind != svn_prop_regular_kind)
181251881Speter    return svn_error_createf
182251881Speter      (SVN_ERR_REPOS_BAD_ARGS, NULL,
183251881Speter       _("Storage of non-regular property '%s' is disallowed through the "
184251881Speter         "repository interface, and could indicate a bug in your client"),
185251881Speter       name);
186251881Speter
187251881Speter  /* Validate "svn:" properties. */
188251881Speter  if (svn_prop_is_svn_prop(name) && value != NULL)
189251881Speter    {
190251881Speter      /* Validate that translated props (e.g., svn:log) are UTF-8 with
191251881Speter       * LF line endings. */
192251881Speter      if (svn_prop_needs_translation(name))
193251881Speter        {
194251881Speter          if (!svn_utf__is_valid(value->data, value->len))
195251881Speter            {
196251881Speter              return svn_error_createf
197251881Speter                (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
198251881Speter                 _("Cannot accept '%s' property because it is not encoded in "
199251881Speter                   "UTF-8"), name);
200251881Speter            }
201251881Speter
202251881Speter          /* Disallow inconsistent line ending style, by simply looking for
203251881Speter           * carriage return characters ('\r'). */
204251881Speter          if (strchr(value->data, '\r') != NULL)
205251881Speter            {
206251881Speter              return svn_error_createf
207251881Speter                (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
208251881Speter                 _("Cannot accept non-LF line endings in '%s' property"),
209251881Speter                   name);
210251881Speter            }
211251881Speter        }
212251881Speter
213251881Speter      /* "svn:date" should be a valid date. */
214251881Speter      if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
215251881Speter        {
216251881Speter          apr_time_t temp;
217251881Speter          svn_error_t *err;
218251881Speter
219251881Speter          err = svn_time_from_cstring(&temp, value->data, pool);
220251881Speter          if (err)
221251881Speter            return svn_error_create(SVN_ERR_BAD_PROPERTY_VALUE,
222251881Speter                                    err, NULL);
223251881Speter        }
224251881Speter    }
225251881Speter
226251881Speter  return SVN_NO_ERROR;
227251881Speter}
228251881Speter
229251881Speter
230251881Speter/* Verify the mergeinfo property value VALUE and return an error if it
231251881Speter * is invalid. The PATH on which that property is set is used for error
232251881Speter * messages only.  Use SCRATCH_POOL for temporary allocations. */
233251881Speterstatic svn_error_t *
234251881Speterverify_mergeinfo(const svn_string_t *value,
235251881Speter                 const char *path,
236251881Speter                 apr_pool_t *scratch_pool)
237251881Speter{
238251881Speter  svn_error_t *err;
239251881Speter  svn_mergeinfo_t mergeinfo;
240251881Speter
241251881Speter  /* It's okay to delete svn:mergeinfo. */
242251881Speter  if (value == NULL)
243251881Speter    return SVN_NO_ERROR;
244251881Speter
245251881Speter  /* Mergeinfo is UTF-8 encoded so the number of bytes returned by strlen()
246251881Speter   * should match VALUE->LEN. Prevents trailing garbage in the property. */
247251881Speter  if (strlen(value->data) != value->len)
248251881Speter    return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
249251881Speter                             _("Commit rejected because mergeinfo on '%s' "
250251881Speter                               "contains unexpected string terminator"),
251251881Speter                             path);
252251881Speter
253251881Speter  err = svn_mergeinfo_parse(&mergeinfo, value->data, scratch_pool);
254251881Speter  if (err)
255251881Speter    return svn_error_createf(err->apr_err, err,
256251881Speter                             _("Commit rejected because mergeinfo on '%s' "
257251881Speter                               "is syntactically invalid"),
258251881Speter                             path);
259251881Speter  return SVN_NO_ERROR;
260251881Speter}
261251881Speter
262251881Speter
263251881Spetersvn_error_t *
264251881Spetersvn_repos_fs_change_node_prop(svn_fs_root_t *root,
265251881Speter                              const char *path,
266251881Speter                              const char *name,
267251881Speter                              const svn_string_t *value,
268251881Speter                              apr_pool_t *pool)
269251881Speter{
270251881Speter  if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
271251881Speter    SVN_ERR(verify_mergeinfo(value, path, pool));
272251881Speter
273251881Speter  /* Validate the property, then call the wrapped function. */
274251881Speter  SVN_ERR(svn_repos__validate_prop(name, value, pool));
275251881Speter  return svn_fs_change_node_prop(root, path, name, value, pool);
276251881Speter}
277251881Speter
278251881Speter
279251881Spetersvn_error_t *
280251881Spetersvn_repos_fs_change_txn_props(svn_fs_txn_t *txn,
281251881Speter                              const apr_array_header_t *txnprops,
282251881Speter                              apr_pool_t *pool)
283251881Speter{
284251881Speter  int i;
285251881Speter
286251881Speter  for (i = 0; i < txnprops->nelts; i++)
287251881Speter    {
288251881Speter      svn_prop_t *prop = &APR_ARRAY_IDX(txnprops, i, svn_prop_t);
289251881Speter      SVN_ERR(svn_repos__validate_prop(prop->name, prop->value, pool));
290251881Speter    }
291251881Speter
292251881Speter  return svn_fs_change_txn_props(txn, txnprops, pool);
293251881Speter}
294251881Speter
295251881Speter
296251881Spetersvn_error_t *
297251881Spetersvn_repos_fs_change_txn_prop(svn_fs_txn_t *txn,
298251881Speter                             const char *name,
299251881Speter                             const svn_string_t *value,
300251881Speter                             apr_pool_t *pool)
301251881Speter{
302251881Speter  apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t));
303251881Speter  svn_prop_t prop;
304251881Speter
305251881Speter  prop.name = name;
306251881Speter  prop.value = value;
307251881Speter  APR_ARRAY_PUSH(props, svn_prop_t) = prop;
308251881Speter
309251881Speter  return svn_repos_fs_change_txn_props(txn, props, pool);
310251881Speter}
311251881Speter
312251881Speter
313251881Spetersvn_error_t *
314251881Spetersvn_repos_fs_change_rev_prop4(svn_repos_t *repos,
315251881Speter                              svn_revnum_t rev,
316251881Speter                              const char *author,
317251881Speter                              const char *name,
318251881Speter                              const svn_string_t *const *old_value_p,
319251881Speter                              const svn_string_t *new_value,
320251881Speter                              svn_boolean_t use_pre_revprop_change_hook,
321251881Speter                              svn_boolean_t use_post_revprop_change_hook,
322251881Speter                              svn_repos_authz_func_t authz_read_func,
323251881Speter                              void *authz_read_baton,
324251881Speter                              apr_pool_t *pool)
325251881Speter{
326251881Speter  svn_repos_revision_access_level_t readability;
327251881Speter
328251881Speter  SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
329251881Speter                                          authz_read_func, authz_read_baton,
330251881Speter                                          pool));
331251881Speter
332251881Speter  if (readability == svn_repos_revision_access_full)
333251881Speter    {
334251881Speter      const svn_string_t *old_value;
335251881Speter      char action;
336251881Speter      apr_hash_t *hooks_env;
337251881Speter
338251881Speter      SVN_ERR(svn_repos__validate_prop(name, new_value, pool));
339251881Speter
340251881Speter      /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */
341251881Speter      if (old_value_p)
342251881Speter        {
343251881Speter          old_value = *old_value_p;
344251881Speter        }
345251881Speter      else
346251881Speter        {
347251881Speter          /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments
348251881Speter           * to the hooks to be accurate. */
349251881Speter          svn_string_t *old_value2;
350251881Speter
351251881Speter          SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool));
352251881Speter          old_value = old_value2;
353251881Speter        }
354251881Speter
355251881Speter      /* Prepare ACTION. */
356251881Speter      if (! new_value)
357251881Speter        action = 'D';
358251881Speter      else if (! old_value)
359251881Speter        action = 'A';
360251881Speter      else
361251881Speter        action = 'M';
362251881Speter
363251881Speter      /* Parse the hooks-env file (if any, and if to be used). */
364251881Speter      if (use_pre_revprop_change_hook || use_post_revprop_change_hook)
365251881Speter        SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
366251881Speter                                           pool, pool));
367251881Speter
368251881Speter      /* ### currently not passing the old_value to hooks */
369251881Speter      if (use_pre_revprop_change_hook)
370251881Speter        SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev,
371251881Speter                                                    author, name, new_value,
372251881Speter                                                    action, pool));
373251881Speter
374251881Speter      SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name,
375251881Speter                                      &old_value, new_value, pool));
376251881Speter
377251881Speter      if (use_post_revprop_change_hook)
378251881Speter        SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev,
379251881Speter                                                     author, name, old_value,
380251881Speter                                                     action, pool));
381251881Speter    }
382251881Speter  else  /* rev is either unreadable or only partially readable */
383251881Speter    {
384251881Speter      return svn_error_createf
385251881Speter        (SVN_ERR_AUTHZ_UNREADABLE, NULL,
386251881Speter         _("Write denied:  not authorized to read all of revision %ld"), rev);
387251881Speter    }
388251881Speter
389251881Speter  return SVN_NO_ERROR;
390251881Speter}
391251881Speter
392251881Speter
393251881Spetersvn_error_t *
394251881Spetersvn_repos_fs_revision_prop(svn_string_t **value_p,
395251881Speter                           svn_repos_t *repos,
396251881Speter                           svn_revnum_t rev,
397251881Speter                           const char *propname,
398251881Speter                           svn_repos_authz_func_t authz_read_func,
399251881Speter                           void *authz_read_baton,
400251881Speter                           apr_pool_t *pool)
401251881Speter{
402251881Speter  svn_repos_revision_access_level_t readability;
403251881Speter
404251881Speter  SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
405251881Speter                                          authz_read_func, authz_read_baton,
406251881Speter                                          pool));
407251881Speter
408251881Speter  if (readability == svn_repos_revision_access_none)
409251881Speter    {
410251881Speter      /* Property?  What property? */
411251881Speter      *value_p = NULL;
412251881Speter    }
413251881Speter  else if (readability == svn_repos_revision_access_partial)
414251881Speter    {
415251881Speter      /* Only svn:author and svn:date are fetchable. */
416251881Speter      if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) != 0)
417251881Speter          && (strcmp(propname, SVN_PROP_REVISION_DATE) != 0))
418251881Speter        *value_p = NULL;
419251881Speter
420251881Speter      else
421251881Speter        SVN_ERR(svn_fs_revision_prop(value_p, repos->fs,
422251881Speter                                     rev, propname, pool));
423251881Speter    }
424251881Speter  else /* wholly readable revision */
425251881Speter    {
426251881Speter      SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool));
427251881Speter    }
428251881Speter
429251881Speter  return SVN_NO_ERROR;
430251881Speter}
431251881Speter
432251881Speter
433251881Speter
434251881Spetersvn_error_t *
435251881Spetersvn_repos_fs_revision_proplist(apr_hash_t **table_p,
436251881Speter                               svn_repos_t *repos,
437251881Speter                               svn_revnum_t rev,
438251881Speter                               svn_repos_authz_func_t authz_read_func,
439251881Speter                               void *authz_read_baton,
440251881Speter                               apr_pool_t *pool)
441251881Speter{
442251881Speter  svn_repos_revision_access_level_t readability;
443251881Speter
444251881Speter  SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
445251881Speter                                          authz_read_func, authz_read_baton,
446251881Speter                                          pool));
447251881Speter
448251881Speter  if (readability == svn_repos_revision_access_none)
449251881Speter    {
450251881Speter      /* Return an empty hash. */
451251881Speter      *table_p = apr_hash_make(pool);
452251881Speter    }
453251881Speter  else if (readability == svn_repos_revision_access_partial)
454251881Speter    {
455251881Speter      apr_hash_t *tmphash;
456251881Speter      svn_string_t *value;
457251881Speter
458251881Speter      /* Produce two property hashtables, both in POOL. */
459251881Speter      SVN_ERR(svn_fs_revision_proplist(&tmphash, repos->fs, rev, pool));
460251881Speter      *table_p = apr_hash_make(pool);
461251881Speter
462251881Speter      /* If they exist, we only copy svn:author and svn:date into the
463251881Speter         'real' hashtable being returned. */
464251881Speter      value = svn_hash_gets(tmphash, SVN_PROP_REVISION_AUTHOR);
465251881Speter      if (value)
466251881Speter        svn_hash_sets(*table_p, SVN_PROP_REVISION_AUTHOR, value);
467251881Speter
468251881Speter      value = svn_hash_gets(tmphash, SVN_PROP_REVISION_DATE);
469251881Speter      if (value)
470251881Speter        svn_hash_sets(*table_p, SVN_PROP_REVISION_DATE, value);
471251881Speter    }
472251881Speter  else /* wholly readable revision */
473251881Speter    {
474251881Speter      SVN_ERR(svn_fs_revision_proplist(table_p, repos->fs, rev, pool));
475251881Speter    }
476251881Speter
477251881Speter  return SVN_NO_ERROR;
478251881Speter}
479251881Speter
480251881Spetersvn_error_t *
481251881Spetersvn_repos_fs_lock(svn_lock_t **lock,
482251881Speter                  svn_repos_t *repos,
483251881Speter                  const char *path,
484251881Speter                  const char *token,
485251881Speter                  const char *comment,
486251881Speter                  svn_boolean_t is_dav_comment,
487251881Speter                  apr_time_t expiration_date,
488251881Speter                  svn_revnum_t current_rev,
489251881Speter                  svn_boolean_t steal_lock,
490251881Speter                  apr_pool_t *pool)
491251881Speter{
492251881Speter  svn_error_t *err;
493251881Speter  svn_fs_access_t *access_ctx = NULL;
494251881Speter  const char *username = NULL;
495251881Speter  const char *new_token;
496251881Speter  apr_array_header_t *paths;
497251881Speter  apr_hash_t *hooks_env;
498251881Speter
499251881Speter  /* Parse the hooks-env file (if any). */
500251881Speter  SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
501251881Speter                                     pool, pool));
502251881Speter
503251881Speter  /* Setup an array of paths in anticipation of the ra layers handling
504251881Speter     multiple locks in one request (1.3 most likely).  This is only
505251881Speter     used by svn_repos__hooks_post_lock. */
506251881Speter  paths = apr_array_make(pool, 1, sizeof(const char *));
507251881Speter  APR_ARRAY_PUSH(paths, const char *) = path;
508251881Speter
509251881Speter  SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
510251881Speter  if (access_ctx)
511251881Speter    SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
512251881Speter
513251881Speter  if (! username)
514251881Speter    return svn_error_createf
515251881Speter      (SVN_ERR_FS_NO_USER, NULL,
516251881Speter       "Cannot lock path '%s', no authenticated username available.", path);
517251881Speter
518251881Speter  /* Run pre-lock hook.  This could throw error, preventing
519251881Speter     svn_fs_lock() from happening. */
520251881Speter  SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
521251881Speter                                    username, comment, steal_lock, pool));
522251881Speter  if (*new_token)
523251881Speter    token = new_token;
524251881Speter
525251881Speter  /* Lock. */
526251881Speter  SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment,
527251881Speter                      expiration_date, current_rev, steal_lock, pool));
528251881Speter
529251881Speter  /* Run post-lock hook. */
530251881Speter  if ((err = svn_repos__hooks_post_lock(repos, hooks_env,
531251881Speter                                        paths, username, pool)))
532251881Speter    return svn_error_create
533251881Speter      (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err,
534251881Speter       "Lock succeeded, but post-lock hook failed");
535251881Speter
536251881Speter  return SVN_NO_ERROR;
537251881Speter}
538251881Speter
539251881Speter
540251881Spetersvn_error_t *
541251881Spetersvn_repos_fs_unlock(svn_repos_t *repos,
542251881Speter                    const char *path,
543251881Speter                    const char *token,
544251881Speter                    svn_boolean_t break_lock,
545251881Speter                    apr_pool_t *pool)
546251881Speter{
547251881Speter  svn_error_t *err;
548251881Speter  svn_fs_access_t *access_ctx = NULL;
549251881Speter  const char *username = NULL;
550251881Speter  apr_array_header_t *paths;
551251881Speter  apr_hash_t *hooks_env;
552251881Speter
553251881Speter  /* Parse the hooks-env file (if any). */
554251881Speter  SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
555251881Speter                                     pool, pool));
556251881Speter
557251881Speter  /* Setup an array of paths in anticipation of the ra layers handling
558251881Speter     multiple locks in one request (1.3 most likely).  This is only
559251881Speter     used by svn_repos__hooks_post_lock. */
560251881Speter  paths = apr_array_make(pool, 1, sizeof(const char *));
561251881Speter  APR_ARRAY_PUSH(paths, const char *) = path;
562251881Speter
563251881Speter  SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
564251881Speter  if (access_ctx)
565251881Speter    SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
566251881Speter
567251881Speter  if (! break_lock && ! username)
568251881Speter    return svn_error_createf
569251881Speter      (SVN_ERR_FS_NO_USER, NULL,
570251881Speter       _("Cannot unlock path '%s', no authenticated username available"),
571251881Speter       path);
572251881Speter
573251881Speter  /* Run pre-unlock hook.  This could throw error, preventing
574251881Speter     svn_fs_unlock() from happening. */
575251881Speter  SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
576251881Speter                                      break_lock, pool));
577251881Speter
578251881Speter  /* Unlock. */
579251881Speter  SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool));
580251881Speter
581251881Speter  /* Run post-unlock hook. */
582251881Speter  if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths,
583251881Speter                                          username, pool)))
584251881Speter    return svn_error_create
585251881Speter      (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err,
586251881Speter       _("Unlock succeeded, but post-unlock hook failed"));
587251881Speter
588251881Speter  return SVN_NO_ERROR;
589251881Speter}
590251881Speter
591251881Speter
592251881Speterstruct get_locks_baton_t
593251881Speter{
594251881Speter  svn_fs_t *fs;
595251881Speter  svn_fs_root_t *head_root;
596251881Speter  svn_repos_authz_func_t authz_read_func;
597251881Speter  void *authz_read_baton;
598251881Speter  apr_hash_t *locks;
599251881Speter};
600251881Speter
601251881Speter
602251881Speter/* This implements the svn_fs_get_locks_callback_t interface. */
603251881Speterstatic svn_error_t *
604251881Speterget_locks_callback(void *baton,
605251881Speter                   svn_lock_t *lock,
606251881Speter                   apr_pool_t *pool)
607251881Speter{
608251881Speter  struct get_locks_baton_t *b = baton;
609251881Speter  svn_boolean_t readable = TRUE;
610251881Speter  apr_pool_t *hash_pool = apr_hash_pool_get(b->locks);
611251881Speter
612251881Speter  /* If there's auth to deal with, deal with it. */
613251881Speter  if (b->authz_read_func)
614251881Speter    SVN_ERR(b->authz_read_func(&readable, b->head_root, lock->path,
615251881Speter                               b->authz_read_baton, pool));
616251881Speter
617251881Speter  /* If we can read this lock path, add the lock to the return hash. */
618251881Speter  if (readable)
619251881Speter    svn_hash_sets(b->locks, apr_pstrdup(hash_pool, lock->path),
620251881Speter                  svn_lock_dup(lock, hash_pool));
621251881Speter
622251881Speter  return SVN_NO_ERROR;
623251881Speter}
624251881Speter
625251881Speter
626251881Spetersvn_error_t *
627251881Spetersvn_repos_fs_get_locks2(apr_hash_t **locks,
628251881Speter                        svn_repos_t *repos,
629251881Speter                        const char *path,
630251881Speter                        svn_depth_t depth,
631251881Speter                        svn_repos_authz_func_t authz_read_func,
632251881Speter                        void *authz_read_baton,
633251881Speter                        apr_pool_t *pool)
634251881Speter{
635251881Speter  apr_hash_t *all_locks = apr_hash_make(pool);
636251881Speter  svn_revnum_t head_rev;
637251881Speter  struct get_locks_baton_t baton;
638251881Speter
639251881Speter  SVN_ERR_ASSERT((depth == svn_depth_empty) ||
640251881Speter                 (depth == svn_depth_files) ||
641251881Speter                 (depth == svn_depth_immediates) ||
642251881Speter                 (depth == svn_depth_infinity));
643251881Speter
644251881Speter  SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool));
645251881Speter
646251881Speter  /* Populate our callback baton. */
647251881Speter  baton.fs = repos->fs;
648251881Speter  baton.locks = all_locks;
649251881Speter  baton.authz_read_func = authz_read_func;
650251881Speter  baton.authz_read_baton = authz_read_baton;
651251881Speter  SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs,
652251881Speter                               head_rev, pool));
653251881Speter
654251881Speter  /* Get all the locks. */
655251881Speter  SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth,
656251881Speter                            get_locks_callback, &baton, pool));
657251881Speter
658251881Speter  *locks = baton.locks;
659251881Speter  return SVN_NO_ERROR;
660251881Speter}
661251881Speter
662251881Speter
663251881Spetersvn_error_t *
664251881Spetersvn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo,
665251881Speter                           svn_repos_t *repos,
666251881Speter                           const apr_array_header_t *paths,
667251881Speter                           svn_revnum_t rev,
668251881Speter                           svn_mergeinfo_inheritance_t inherit,
669251881Speter                           svn_boolean_t include_descendants,
670251881Speter                           svn_repos_authz_func_t authz_read_func,
671251881Speter                           void *authz_read_baton,
672251881Speter                           apr_pool_t *pool)
673251881Speter{
674251881Speter  /* Here we cast away 'const', but won't try to write through this pointer
675251881Speter   * without first allocating a new array. */
676251881Speter  apr_array_header_t *readable_paths = (apr_array_header_t *) paths;
677251881Speter  svn_fs_root_t *root;
678251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
679251881Speter
680251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
681251881Speter    SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool));
682251881Speter  SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool));
683251881Speter
684251881Speter  /* Filter out unreadable paths before divining merge tracking info. */
685251881Speter  if (authz_read_func)
686251881Speter    {
687251881Speter      int i;
688251881Speter
689251881Speter      for (i = 0; i < paths->nelts; i++)
690251881Speter        {
691251881Speter          svn_boolean_t readable;
692251881Speter          const char *path = APR_ARRAY_IDX(paths, i, char *);
693251881Speter          svn_pool_clear(iterpool);
694251881Speter          SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton,
695251881Speter                                  iterpool));
696251881Speter          if (readable && readable_paths != paths)
697251881Speter            APR_ARRAY_PUSH(readable_paths, const char *) = path;
698251881Speter          else if (!readable && readable_paths == paths)
699251881Speter            {
700251881Speter              /* Requested paths differ from readable paths.  Fork
701251881Speter                 list of readable paths from requested paths. */
702251881Speter              int j;
703251881Speter              readable_paths = apr_array_make(pool, paths->nelts - 1,
704251881Speter                                              sizeof(char *));
705251881Speter              for (j = 0; j < i; j++)
706251881Speter                {
707251881Speter                  path = APR_ARRAY_IDX(paths, j, char *);
708251881Speter                  APR_ARRAY_PUSH(readable_paths, const char *) = path;
709251881Speter                }
710251881Speter            }
711251881Speter        }
712251881Speter    }
713251881Speter
714251881Speter  /* We consciously do not perform authz checks on the paths returned
715251881Speter     in *MERGEINFO, avoiding massive authz overhead which would allow
716251881Speter     us to protect the name of where a change was merged from, but not
717251881Speter     the change itself. */
718251881Speter  /* ### TODO(reint): ... but how about descendant merged-to paths? */
719251881Speter  if (readable_paths->nelts > 0)
720251881Speter    SVN_ERR(svn_fs_get_mergeinfo2(mergeinfo, root, readable_paths, inherit,
721251881Speter                                  include_descendants, TRUE, pool, pool));
722251881Speter  else
723251881Speter    *mergeinfo = apr_hash_make(pool);
724251881Speter
725251881Speter  svn_pool_destroy(iterpool);
726251881Speter  return SVN_NO_ERROR;
727251881Speter}
728251881Speter
729251881Speterstruct pack_notify_baton
730251881Speter{
731251881Speter  svn_repos_notify_func_t notify_func;
732251881Speter  void *notify_baton;
733251881Speter};
734251881Speter
735251881Speter/* Implements svn_fs_pack_notify_t. */
736251881Speterstatic svn_error_t *
737251881Speterpack_notify_func(void *baton,
738251881Speter                 apr_int64_t shard,
739251881Speter                 svn_fs_pack_notify_action_t pack_action,
740251881Speter                 apr_pool_t *pool)
741251881Speter{
742251881Speter  struct pack_notify_baton *pnb = baton;
743251881Speter  svn_repos_notify_t *notify;
744251881Speter
745251881Speter  /* Simple conversion works for these values. */
746251881Speter  SVN_ERR_ASSERT(pack_action >= svn_fs_pack_notify_start
747251881Speter                 && pack_action <= svn_fs_pack_notify_end_revprop);
748251881Speter
749251881Speter  notify = svn_repos_notify_create(pack_action
750251881Speter                                   + svn_repos_notify_pack_shard_start
751251881Speter                                   - svn_fs_pack_notify_start,
752251881Speter                                   pool);
753251881Speter  notify->shard = shard;
754251881Speter  pnb->notify_func(pnb->notify_baton, notify, pool);
755251881Speter
756251881Speter  return SVN_NO_ERROR;
757251881Speter}
758251881Speter
759251881Spetersvn_error_t *
760251881Spetersvn_repos_fs_pack2(svn_repos_t *repos,
761251881Speter                   svn_repos_notify_func_t notify_func,
762251881Speter                   void *notify_baton,
763251881Speter                   svn_cancel_func_t cancel_func,
764251881Speter                   void *cancel_baton,
765251881Speter                   apr_pool_t *pool)
766251881Speter{
767251881Speter  struct pack_notify_baton pnb;
768251881Speter
769251881Speter  pnb.notify_func = notify_func;
770251881Speter  pnb.notify_baton = notify_baton;
771251881Speter
772251881Speter  return svn_fs_pack(repos->db_path,
773251881Speter                     notify_func ? pack_notify_func : NULL,
774251881Speter                     notify_func ? &pnb : NULL,
775251881Speter                     cancel_func, cancel_baton, pool);
776251881Speter}
777251881Speter
778251881Spetersvn_error_t *
779251881Spetersvn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p,
780251881Speter                                 svn_fs_root_t *root,
781251881Speter                                 const char *path,
782251881Speter                                 const char *propname,
783251881Speter                                 svn_repos_authz_func_t authz_read_func,
784251881Speter                                 void *authz_read_baton,
785251881Speter                                 apr_pool_t *result_pool,
786251881Speter                                 apr_pool_t *scratch_pool)
787251881Speter{
788251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
789251881Speter  apr_array_header_t *inherited_props;
790251881Speter  const char *parent_path = path;
791251881Speter
792251881Speter  inherited_props = apr_array_make(result_pool, 1,
793251881Speter                                   sizeof(svn_prop_inherited_item_t *));
794251881Speter  while (!(parent_path[0] == '/' && parent_path[1] == '\0'))
795251881Speter    {
796251881Speter      svn_boolean_t allowed = TRUE;
797251881Speter      apr_hash_t *parent_properties = NULL;
798251881Speter
799251881Speter      svn_pool_clear(iterpool);
800251881Speter      parent_path = svn_fspath__dirname(parent_path, scratch_pool);
801251881Speter
802251881Speter      if (authz_read_func)
803251881Speter        SVN_ERR(authz_read_func(&allowed, root, parent_path,
804251881Speter                                authz_read_baton, iterpool));
805251881Speter      if (allowed)
806251881Speter        {
807251881Speter          if (propname)
808251881Speter            {
809251881Speter              svn_string_t *propval;
810251881Speter
811251881Speter              SVN_ERR(svn_fs_node_prop(&propval, root, parent_path, propname,
812251881Speter                                       result_pool));
813251881Speter              if (propval)
814251881Speter                {
815251881Speter                  parent_properties = apr_hash_make(result_pool);
816251881Speter                  svn_hash_sets(parent_properties, propname, propval);
817251881Speter                }
818251881Speter            }
819251881Speter          else
820251881Speter            {
821251881Speter              SVN_ERR(svn_fs_node_proplist(&parent_properties, root,
822251881Speter                                           parent_path, result_pool));
823251881Speter            }
824251881Speter
825251881Speter          if (parent_properties && apr_hash_count(parent_properties))
826251881Speter            {
827251881Speter              svn_prop_inherited_item_t *i_props =
828251881Speter                apr_pcalloc(result_pool, sizeof(*i_props));
829251881Speter              i_props->path_or_url =
830251881Speter                apr_pstrdup(result_pool, parent_path + 1);
831251881Speter              i_props->prop_hash = parent_properties;
832251881Speter              /* Build the output array in depth-first order. */
833251881Speter              svn_sort__array_insert(&i_props, inherited_props, 0);
834251881Speter            }
835251881Speter        }
836251881Speter    }
837251881Speter
838251881Speter  svn_pool_destroy(iterpool);
839251881Speter
840251881Speter  *inherited_props_p = inherited_props;
841251881Speter  return SVN_NO_ERROR;
842251881Speter}
843251881Speter
844251881Speter/*
845251881Speter * vim:ts=4:sw=2:expandtab:tw=80:fo=tcroq
846251881Speter * vim:isk=a-z,A-Z,48-57,_,.,-,>
847251881Speter * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0
848251881Speter */
849