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