]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_repos/fs-wrap.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_repos / fs-wrap.c
1 /* fs-wrap.c --- filesystem interface wrappers.
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26
27 #include "svn_hash.h"
28 #include "svn_pools.h"
29 #include "svn_error.h"
30 #include "svn_fs.h"
31 #include "svn_path.h"
32 #include "svn_props.h"
33 #include "svn_repos.h"
34 #include "svn_time.h"
35 #include "svn_sorts.h"
36 #include "repos.h"
37 #include "svn_private_config.h"
38 #include "private/svn_repos_private.h"
39 #include "private/svn_utf_private.h"
40 #include "private/svn_fspath.h"
41
42 \f
43 /*** Commit wrappers ***/
44
45 svn_error_t *
46 svn_repos_fs_commit_txn(const char **conflict_p,
47                         svn_repos_t *repos,
48                         svn_revnum_t *new_rev,
49                         svn_fs_txn_t *txn,
50                         apr_pool_t *pool)
51 {
52   svn_error_t *err, *err2;
53   const char *txn_name;
54   apr_hash_t *props;
55   apr_pool_t *iterpool;
56   apr_hash_index_t *hi;
57   apr_hash_t *hooks_env;
58
59   *new_rev = SVN_INVALID_REVNUM;
60
61   /* Parse the hooks-env file (if any). */
62   SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
63                                      pool, pool));
64
65   /* Run pre-commit hooks. */
66   SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
67   SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool));
68
69   /* Remove any ephemeral transaction properties. */
70   SVN_ERR(svn_fs_txn_proplist(&props, txn, pool));
71   iterpool = svn_pool_create(pool);
72   for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
73     {
74       const void *key;
75       apr_hash_this(hi, &key, NULL, NULL);
76
77       svn_pool_clear(iterpool);
78
79       if (strncmp(key, SVN_PROP_TXN_PREFIX,
80                   (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
81         {
82           SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool));
83         }
84     }
85   svn_pool_destroy(iterpool);
86
87   /* Commit. */
88   err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool);
89   if (! SVN_IS_VALID_REVNUM(*new_rev))
90     return err;
91
92   /* Run post-commit hooks. */
93   if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env,
94                                            *new_rev, txn_name, pool)))
95     {
96       err2 = svn_error_create
97                (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2,
98                 _("Commit succeeded, but post-commit hook failed"));
99     }
100
101   return svn_error_compose_create(err, err2);
102 }
103
104
105 \f
106 /*** Transaction creation wrappers. ***/
107
108
109 svn_error_t *
110 svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
111                                    svn_repos_t *repos,
112                                    svn_revnum_t rev,
113                                    apr_hash_t *revprop_table,
114                                    apr_pool_t *pool)
115 {
116   apr_array_header_t *revprops;
117   const char *txn_name;
118   svn_string_t *author = svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR);
119   apr_hash_t *hooks_env;
120   svn_error_t *err;
121   svn_fs_txn_t *txn;
122
123   /* Parse the hooks-env file (if any). */
124   SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
125                                      pool, pool));
126
127   /* Begin the transaction, ask for the fs to do on-the-fly lock checks.
128      We fetch its name, too, so the start-commit hook can use it.  */
129   SVN_ERR(svn_fs_begin_txn2(&txn, repos->fs, rev,
130                             SVN_FS_TXN_CHECK_LOCKS, pool));
131   err = svn_fs_txn_name(&txn_name, txn, pool);
132   if (err)
133     return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
134
135   /* We pass the revision properties to the filesystem by adding them
136      as properties on the txn.  Later, when we commit the txn, these
137      properties will be copied into the newly created revision. */
138   revprops = svn_prop_hash_to_array(revprop_table, pool);
139   err = svn_repos_fs_change_txn_props(txn, revprops, pool);
140   if (err)
141     return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
142
143   /* Run start-commit hooks. */
144   err = svn_repos__hooks_start_commit(repos, hooks_env,
145                                       author ? author->data : NULL,
146                                       repos->client_capabilities, txn_name,
147                                       pool);
148   if (err)
149     return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
150
151   /* We have API promise that *TXN_P is unaffected on faulure. */
152   *txn_p = txn;
153   return SVN_NO_ERROR;
154 }
155
156
157 svn_error_t *
158 svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p,
159                                   svn_repos_t *repos,
160                                   svn_revnum_t rev,
161                                   const char *author,
162                                   const char *log_msg,
163                                   apr_pool_t *pool)
164 {
165   apr_hash_t *revprop_table = apr_hash_make(pool);
166   if (author)
167     svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
168                   svn_string_create(author, pool));
169   if (log_msg)
170     svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
171                   svn_string_create(log_msg, pool));
172   return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, revprop_table,
173                                             pool);
174 }
175
176 \f
177 /*** Property wrappers ***/
178
179 svn_error_t *
180 svn_repos__validate_prop(const char *name,
181                          const svn_string_t *value,
182                          apr_pool_t *pool)
183 {
184   svn_prop_kind_t kind = svn_property_kind2(name);
185
186   /* Allow deleting any property, even a property we don't allow to set. */
187   if (value == NULL)
188     return SVN_NO_ERROR;
189
190   /* Disallow setting non-regular properties. */
191   if (kind != svn_prop_regular_kind)
192     return svn_error_createf
193       (SVN_ERR_REPOS_BAD_ARGS, NULL,
194        _("Storage of non-regular property '%s' is disallowed through the "
195          "repository interface, and could indicate a bug in your client"),
196        name);
197
198   /* Validate "svn:" properties. */
199   if (svn_prop_is_svn_prop(name) && value != NULL)
200     {
201       /* Validate that translated props (e.g., svn:log) are UTF-8 with
202        * LF line endings. */
203       if (svn_prop_needs_translation(name))
204         {
205           if (!svn_utf__is_valid(value->data, value->len))
206             {
207               return svn_error_createf
208                 (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
209                  _("Cannot accept '%s' property because it is not encoded in "
210                    "UTF-8"), name);
211             }
212
213           /* Disallow inconsistent line ending style, by simply looking for
214            * carriage return characters ('\r'). */
215           if (strchr(value->data, '\r') != NULL)
216             {
217               return svn_error_createf
218                 (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
219                  _("Cannot accept non-LF line endings in '%s' property"),
220                    name);
221             }
222         }
223
224       /* "svn:date" should be a valid date. */
225       if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
226         {
227           apr_time_t temp;
228           svn_error_t *err;
229
230           err = svn_time_from_cstring(&temp, value->data, pool);
231           if (err)
232             return svn_error_create(SVN_ERR_BAD_PROPERTY_VALUE,
233                                     err, NULL);
234         }
235     }
236
237   return SVN_NO_ERROR;
238 }
239
240
241 /* Verify the mergeinfo property value VALUE and return an error if it
242  * is invalid. The PATH on which that property is set is used for error
243  * messages only.  Use SCRATCH_POOL for temporary allocations. */
244 static svn_error_t *
245 verify_mergeinfo(const svn_string_t *value,
246                  const char *path,
247                  apr_pool_t *scratch_pool)
248 {
249   svn_error_t *err;
250   svn_mergeinfo_t mergeinfo;
251
252   /* It's okay to delete svn:mergeinfo. */
253   if (value == NULL)
254     return SVN_NO_ERROR;
255
256   /* Mergeinfo is UTF-8 encoded so the number of bytes returned by strlen()
257    * should match VALUE->LEN. Prevents trailing garbage in the property. */
258   if (strlen(value->data) != value->len)
259     return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
260                              _("Commit rejected because mergeinfo on '%s' "
261                                "contains unexpected string terminator"),
262                              path);
263
264   err = svn_mergeinfo_parse(&mergeinfo, value->data, scratch_pool);
265   if (err)
266     return svn_error_createf(err->apr_err, err,
267                              _("Commit rejected because mergeinfo on '%s' "
268                                "is syntactically invalid"),
269                              path);
270   return SVN_NO_ERROR;
271 }
272
273
274 svn_error_t *
275 svn_repos_fs_change_node_prop(svn_fs_root_t *root,
276                               const char *path,
277                               const char *name,
278                               const svn_string_t *value,
279                               apr_pool_t *pool)
280 {
281   if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
282     SVN_ERR(verify_mergeinfo(value, path, pool));
283
284   /* Validate the property, then call the wrapped function. */
285   SVN_ERR(svn_repos__validate_prop(name, value, pool));
286   return svn_fs_change_node_prop(root, path, name, value, pool);
287 }
288
289
290 svn_error_t *
291 svn_repos_fs_change_txn_props(svn_fs_txn_t *txn,
292                               const apr_array_header_t *txnprops,
293                               apr_pool_t *pool)
294 {
295   int i;
296
297   for (i = 0; i < txnprops->nelts; i++)
298     {
299       svn_prop_t *prop = &APR_ARRAY_IDX(txnprops, i, svn_prop_t);
300       SVN_ERR(svn_repos__validate_prop(prop->name, prop->value, pool));
301     }
302
303   return svn_fs_change_txn_props(txn, txnprops, pool);
304 }
305
306
307 svn_error_t *
308 svn_repos_fs_change_txn_prop(svn_fs_txn_t *txn,
309                              const char *name,
310                              const svn_string_t *value,
311                              apr_pool_t *pool)
312 {
313   apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t));
314   svn_prop_t prop;
315
316   prop.name = name;
317   prop.value = value;
318   APR_ARRAY_PUSH(props, svn_prop_t) = prop;
319
320   return svn_repos_fs_change_txn_props(txn, props, pool);
321 }
322
323
324 svn_error_t *
325 svn_repos_fs_change_rev_prop4(svn_repos_t *repos,
326                               svn_revnum_t rev,
327                               const char *author,
328                               const char *name,
329                               const svn_string_t *const *old_value_p,
330                               const svn_string_t *new_value,
331                               svn_boolean_t use_pre_revprop_change_hook,
332                               svn_boolean_t use_post_revprop_change_hook,
333                               svn_repos_authz_func_t authz_read_func,
334                               void *authz_read_baton,
335                               apr_pool_t *pool)
336 {
337   svn_repos_revision_access_level_t readability;
338
339   SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
340                                           authz_read_func, authz_read_baton,
341                                           pool));
342
343   if (readability == svn_repos_revision_access_full)
344     {
345       const svn_string_t *old_value;
346       char action;
347       apr_hash_t *hooks_env;
348
349       SVN_ERR(svn_repos__validate_prop(name, new_value, pool));
350
351       /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */
352       if (old_value_p)
353         {
354           old_value = *old_value_p;
355         }
356       else
357         {
358           /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments
359            * to the hooks to be accurate. */
360           svn_string_t *old_value2;
361
362           SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool));
363           old_value = old_value2;
364         }
365
366       /* Prepare ACTION. */
367       if (! new_value)
368         action = 'D';
369       else if (! old_value)
370         action = 'A';
371       else
372         action = 'M';
373
374       /* Parse the hooks-env file (if any, and if to be used). */
375       if (use_pre_revprop_change_hook || use_post_revprop_change_hook)
376         SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
377                                            pool, pool));
378
379       /* ### currently not passing the old_value to hooks */
380       if (use_pre_revprop_change_hook)
381         SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev,
382                                                     author, name, new_value,
383                                                     action, pool));
384
385       SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name,
386                                       &old_value, new_value, pool));
387
388       if (use_post_revprop_change_hook)
389         SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev,
390                                                      author, name, old_value,
391                                                      action, pool));
392     }
393   else  /* rev is either unreadable or only partially readable */
394     {
395       return svn_error_createf
396         (SVN_ERR_AUTHZ_UNREADABLE, NULL,
397          _("Write denied:  not authorized to read all of revision %ld"), rev);
398     }
399
400   return SVN_NO_ERROR;
401 }
402
403
404 svn_error_t *
405 svn_repos_fs_revision_prop(svn_string_t **value_p,
406                            svn_repos_t *repos,
407                            svn_revnum_t rev,
408                            const char *propname,
409                            svn_repos_authz_func_t authz_read_func,
410                            void *authz_read_baton,
411                            apr_pool_t *pool)
412 {
413   svn_repos_revision_access_level_t readability;
414
415   SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
416                                           authz_read_func, authz_read_baton,
417                                           pool));
418
419   if (readability == svn_repos_revision_access_none)
420     {
421       /* Property?  What property? */
422       *value_p = NULL;
423     }
424   else if (readability == svn_repos_revision_access_partial)
425     {
426       /* Only svn:author and svn:date are fetchable. */
427       if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) != 0)
428           && (strcmp(propname, SVN_PROP_REVISION_DATE) != 0))
429         *value_p = NULL;
430
431       else
432         SVN_ERR(svn_fs_revision_prop(value_p, repos->fs,
433                                      rev, propname, pool));
434     }
435   else /* wholly readable revision */
436     {
437       SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool));
438     }
439
440   return SVN_NO_ERROR;
441 }
442
443
444
445 svn_error_t *
446 svn_repos_fs_revision_proplist(apr_hash_t **table_p,
447                                svn_repos_t *repos,
448                                svn_revnum_t rev,
449                                svn_repos_authz_func_t authz_read_func,
450                                void *authz_read_baton,
451                                apr_pool_t *pool)
452 {
453   svn_repos_revision_access_level_t readability;
454
455   SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
456                                           authz_read_func, authz_read_baton,
457                                           pool));
458
459   if (readability == svn_repos_revision_access_none)
460     {
461       /* Return an empty hash. */
462       *table_p = apr_hash_make(pool);
463     }
464   else if (readability == svn_repos_revision_access_partial)
465     {
466       apr_hash_t *tmphash;
467       svn_string_t *value;
468
469       /* Produce two property hashtables, both in POOL. */
470       SVN_ERR(svn_fs_revision_proplist(&tmphash, repos->fs, rev, pool));
471       *table_p = apr_hash_make(pool);
472
473       /* If they exist, we only copy svn:author and svn:date into the
474          'real' hashtable being returned. */
475       value = svn_hash_gets(tmphash, SVN_PROP_REVISION_AUTHOR);
476       if (value)
477         svn_hash_sets(*table_p, SVN_PROP_REVISION_AUTHOR, value);
478
479       value = svn_hash_gets(tmphash, SVN_PROP_REVISION_DATE);
480       if (value)
481         svn_hash_sets(*table_p, SVN_PROP_REVISION_DATE, value);
482     }
483   else /* wholly readable revision */
484     {
485       SVN_ERR(svn_fs_revision_proplist(table_p, repos->fs, rev, pool));
486     }
487
488   return SVN_NO_ERROR;
489 }
490
491 svn_error_t *
492 svn_repos_fs_lock(svn_lock_t **lock,
493                   svn_repos_t *repos,
494                   const char *path,
495                   const char *token,
496                   const char *comment,
497                   svn_boolean_t is_dav_comment,
498                   apr_time_t expiration_date,
499                   svn_revnum_t current_rev,
500                   svn_boolean_t steal_lock,
501                   apr_pool_t *pool)
502 {
503   svn_error_t *err;
504   svn_fs_access_t *access_ctx = NULL;
505   const char *username = NULL;
506   const char *new_token;
507   apr_array_header_t *paths;
508   apr_hash_t *hooks_env;
509
510   /* Parse the hooks-env file (if any). */
511   SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
512                                      pool, pool));
513
514   /* Setup an array of paths in anticipation of the ra layers handling
515      multiple locks in one request (1.3 most likely).  This is only
516      used by svn_repos__hooks_post_lock. */
517   paths = apr_array_make(pool, 1, sizeof(const char *));
518   APR_ARRAY_PUSH(paths, const char *) = path;
519
520   SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
521   if (access_ctx)
522     SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
523
524   if (! username)
525     return svn_error_createf
526       (SVN_ERR_FS_NO_USER, NULL,
527        "Cannot lock path '%s', no authenticated username available.", path);
528
529   /* Run pre-lock hook.  This could throw error, preventing
530      svn_fs_lock() from happening. */
531   SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
532                                     username, comment, steal_lock, pool));
533   if (*new_token)
534     token = new_token;
535
536   /* Lock. */
537   SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment,
538                       expiration_date, current_rev, steal_lock, pool));
539
540   /* Run post-lock hook. */
541   if ((err = svn_repos__hooks_post_lock(repos, hooks_env,
542                                         paths, username, pool)))
543     return svn_error_create
544       (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err,
545        "Lock succeeded, but post-lock hook failed");
546
547   return SVN_NO_ERROR;
548 }
549
550
551 svn_error_t *
552 svn_repos_fs_unlock(svn_repos_t *repos,
553                     const char *path,
554                     const char *token,
555                     svn_boolean_t break_lock,
556                     apr_pool_t *pool)
557 {
558   svn_error_t *err;
559   svn_fs_access_t *access_ctx = NULL;
560   const char *username = NULL;
561   apr_array_header_t *paths;
562   apr_hash_t *hooks_env;
563
564   /* Parse the hooks-env file (if any). */
565   SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
566                                      pool, pool));
567
568   /* Setup an array of paths in anticipation of the ra layers handling
569      multiple locks in one request (1.3 most likely).  This is only
570      used by svn_repos__hooks_post_lock. */
571   paths = apr_array_make(pool, 1, sizeof(const char *));
572   APR_ARRAY_PUSH(paths, const char *) = path;
573
574   SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
575   if (access_ctx)
576     SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
577
578   if (! break_lock && ! username)
579     return svn_error_createf
580       (SVN_ERR_FS_NO_USER, NULL,
581        _("Cannot unlock path '%s', no authenticated username available"),
582        path);
583
584   /* Run pre-unlock hook.  This could throw error, preventing
585      svn_fs_unlock() from happening. */
586   SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
587                                       break_lock, pool));
588
589   /* Unlock. */
590   SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool));
591
592   /* Run post-unlock hook. */
593   if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths,
594                                           username, pool)))
595     return svn_error_create
596       (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err,
597        _("Unlock succeeded, but post-unlock hook failed"));
598
599   return SVN_NO_ERROR;
600 }
601
602
603 struct get_locks_baton_t
604 {
605   svn_fs_t *fs;
606   svn_fs_root_t *head_root;
607   svn_repos_authz_func_t authz_read_func;
608   void *authz_read_baton;
609   apr_hash_t *locks;
610 };
611
612
613 /* This implements the svn_fs_get_locks_callback_t interface. */
614 static svn_error_t *
615 get_locks_callback(void *baton,
616                    svn_lock_t *lock,
617                    apr_pool_t *pool)
618 {
619   struct get_locks_baton_t *b = baton;
620   svn_boolean_t readable = TRUE;
621   apr_pool_t *hash_pool = apr_hash_pool_get(b->locks);
622
623   /* If there's auth to deal with, deal with it. */
624   if (b->authz_read_func)
625     SVN_ERR(b->authz_read_func(&readable, b->head_root, lock->path,
626                                b->authz_read_baton, pool));
627
628   /* If we can read this lock path, add the lock to the return hash. */
629   if (readable)
630     svn_hash_sets(b->locks, apr_pstrdup(hash_pool, lock->path),
631                   svn_lock_dup(lock, hash_pool));
632
633   return SVN_NO_ERROR;
634 }
635
636
637 svn_error_t *
638 svn_repos_fs_get_locks2(apr_hash_t **locks,
639                         svn_repos_t *repos,
640                         const char *path,
641                         svn_depth_t depth,
642                         svn_repos_authz_func_t authz_read_func,
643                         void *authz_read_baton,
644                         apr_pool_t *pool)
645 {
646   apr_hash_t *all_locks = apr_hash_make(pool);
647   svn_revnum_t head_rev;
648   struct get_locks_baton_t baton;
649
650   SVN_ERR_ASSERT((depth == svn_depth_empty) ||
651                  (depth == svn_depth_files) ||
652                  (depth == svn_depth_immediates) ||
653                  (depth == svn_depth_infinity));
654
655   SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool));
656
657   /* Populate our callback baton. */
658   baton.fs = repos->fs;
659   baton.locks = all_locks;
660   baton.authz_read_func = authz_read_func;
661   baton.authz_read_baton = authz_read_baton;
662   SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs,
663                                head_rev, pool));
664
665   /* Get all the locks. */
666   SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth,
667                             get_locks_callback, &baton, pool));
668
669   *locks = baton.locks;
670   return SVN_NO_ERROR;
671 }
672
673
674 svn_error_t *
675 svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo,
676                            svn_repos_t *repos,
677                            const apr_array_header_t *paths,
678                            svn_revnum_t rev,
679                            svn_mergeinfo_inheritance_t inherit,
680                            svn_boolean_t include_descendants,
681                            svn_repos_authz_func_t authz_read_func,
682                            void *authz_read_baton,
683                            apr_pool_t *pool)
684 {
685   /* Here we cast away 'const', but won't try to write through this pointer
686    * without first allocating a new array. */
687   apr_array_header_t *readable_paths = (apr_array_header_t *) paths;
688   svn_fs_root_t *root;
689   apr_pool_t *iterpool = svn_pool_create(pool);
690
691   if (!SVN_IS_VALID_REVNUM(rev))
692     SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool));
693   SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool));
694
695   /* Filter out unreadable paths before divining merge tracking info. */
696   if (authz_read_func)
697     {
698       int i;
699
700       for (i = 0; i < paths->nelts; i++)
701         {
702           svn_boolean_t readable;
703           const char *path = APR_ARRAY_IDX(paths, i, char *);
704           svn_pool_clear(iterpool);
705           SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton,
706                                   iterpool));
707           if (readable && readable_paths != paths)
708             APR_ARRAY_PUSH(readable_paths, const char *) = path;
709           else if (!readable && readable_paths == paths)
710             {
711               /* Requested paths differ from readable paths.  Fork
712                  list of readable paths from requested paths. */
713               int j;
714               readable_paths = apr_array_make(pool, paths->nelts - 1,
715                                               sizeof(char *));
716               for (j = 0; j < i; j++)
717                 {
718                   path = APR_ARRAY_IDX(paths, j, char *);
719                   APR_ARRAY_PUSH(readable_paths, const char *) = path;
720                 }
721             }
722         }
723     }
724
725   /* We consciously do not perform authz checks on the paths returned
726      in *MERGEINFO, avoiding massive authz overhead which would allow
727      us to protect the name of where a change was merged from, but not
728      the change itself. */
729   /* ### TODO(reint): ... but how about descendant merged-to paths? */
730   if (readable_paths->nelts > 0)
731     SVN_ERR(svn_fs_get_mergeinfo2(mergeinfo, root, readable_paths, inherit,
732                                   include_descendants, TRUE, pool, pool));
733   else
734     *mergeinfo = apr_hash_make(pool);
735
736   svn_pool_destroy(iterpool);
737   return SVN_NO_ERROR;
738 }
739
740 struct pack_notify_baton
741 {
742   svn_repos_notify_func_t notify_func;
743   void *notify_baton;
744 };
745
746 /* Implements svn_fs_pack_notify_t. */
747 static svn_error_t *
748 pack_notify_func(void *baton,
749                  apr_int64_t shard,
750                  svn_fs_pack_notify_action_t pack_action,
751                  apr_pool_t *pool)
752 {
753   struct pack_notify_baton *pnb = baton;
754   svn_repos_notify_t *notify;
755
756   /* Simple conversion works for these values. */
757   SVN_ERR_ASSERT(pack_action >= svn_fs_pack_notify_start
758                  && pack_action <= svn_fs_pack_notify_end_revprop);
759
760   notify = svn_repos_notify_create(pack_action
761                                    + svn_repos_notify_pack_shard_start
762                                    - svn_fs_pack_notify_start,
763                                    pool);
764   notify->shard = shard;
765   pnb->notify_func(pnb->notify_baton, notify, pool);
766
767   return SVN_NO_ERROR;
768 }
769
770 svn_error_t *
771 svn_repos_fs_pack2(svn_repos_t *repos,
772                    svn_repos_notify_func_t notify_func,
773                    void *notify_baton,
774                    svn_cancel_func_t cancel_func,
775                    void *cancel_baton,
776                    apr_pool_t *pool)
777 {
778   struct pack_notify_baton pnb;
779
780   pnb.notify_func = notify_func;
781   pnb.notify_baton = notify_baton;
782
783   return svn_fs_pack(repos->db_path,
784                      notify_func ? pack_notify_func : NULL,
785                      notify_func ? &pnb : NULL,
786                      cancel_func, cancel_baton, pool);
787 }
788
789 svn_error_t *
790 svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p,
791                                  svn_fs_root_t *root,
792                                  const char *path,
793                                  const char *propname,
794                                  svn_repos_authz_func_t authz_read_func,
795                                  void *authz_read_baton,
796                                  apr_pool_t *result_pool,
797                                  apr_pool_t *scratch_pool)
798 {
799   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
800   apr_array_header_t *inherited_props;
801   const char *parent_path = path;
802
803   inherited_props = apr_array_make(result_pool, 1,
804                                    sizeof(svn_prop_inherited_item_t *));
805   while (!(parent_path[0] == '/' && parent_path[1] == '\0'))
806     {
807       svn_boolean_t allowed = TRUE;
808       apr_hash_t *parent_properties = NULL;
809
810       svn_pool_clear(iterpool);
811       parent_path = svn_fspath__dirname(parent_path, scratch_pool);
812
813       if (authz_read_func)
814         SVN_ERR(authz_read_func(&allowed, root, parent_path,
815                                 authz_read_baton, iterpool));
816       if (allowed)
817         {
818           if (propname)
819             {
820               svn_string_t *propval;
821
822               SVN_ERR(svn_fs_node_prop(&propval, root, parent_path, propname,
823                                        result_pool));
824               if (propval)
825                 {
826                   parent_properties = apr_hash_make(result_pool);
827                   svn_hash_sets(parent_properties, propname, propval);
828                 }
829             }
830           else
831             {
832               SVN_ERR(svn_fs_node_proplist(&parent_properties, root,
833                                            parent_path, result_pool));
834             }
835
836           if (parent_properties && apr_hash_count(parent_properties))
837             {
838               svn_prop_inherited_item_t *i_props =
839                 apr_pcalloc(result_pool, sizeof(*i_props));
840               i_props->path_or_url =
841                 apr_pstrdup(result_pool, parent_path + 1);
842               i_props->prop_hash = parent_properties;
843               /* Build the output array in depth-first order. */
844               svn_sort__array_insert(&i_props, inherited_props, 0);
845             }
846         }
847     }
848
849   svn_pool_destroy(iterpool);
850
851   *inherited_props_p = inherited_props;
852   return SVN_NO_ERROR;
853 }
854 \f
855 /*
856  * vim:ts=4:sw=2:expandtab:tw=80:fo=tcroq
857  * vim:isk=a-z,A-Z,48-57,_,.,-,>
858  * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0
859  */