1 /* fs-wrap.c --- filesystem interface wrappers.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * ====================================================================
28 #include "svn_pools.h"
29 #include "svn_error.h"
32 #include "svn_props.h"
33 #include "svn_repos.h"
35 #include "svn_sorts.h"
37 #include "svn_private_config.h"
38 #include "private/svn_repos_private.h"
39 #include "private/svn_sorts_private.h"
40 #include "private/svn_utf_private.h"
41 #include "private/svn_fspath.h"
44 /*** Commit wrappers ***/
47 svn_repos_fs_commit_txn(const char **conflict_p,
49 svn_revnum_t *new_rev,
53 svn_error_t *err, *err2;
58 apr_hash_t *hooks_env;
60 *new_rev = SVN_INVALID_REVNUM;
62 /* Parse the hooks-env file (if any). */
63 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
66 /* Run pre-commit hooks. */
67 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
68 SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool));
70 /* Remove any ephemeral transaction properties. If the commit fails
71 we will attempt to restore the properties but if that fails, or
72 the process is killed, the properties will be lost. */
73 SVN_ERR(svn_fs_txn_proplist(&props, txn, pool));
74 iterpool = svn_pool_create(pool);
75 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
77 const char *key = apr_hash_this_key(hi);
79 svn_pool_clear(iterpool);
81 if (strncmp(key, SVN_PROP_TXN_PREFIX,
82 (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
84 SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool));
87 svn_pool_destroy(iterpool);
90 err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool);
91 if (! SVN_IS_VALID_REVNUM(*new_rev))
93 /* The commit failed, try to restore the ephemeral properties. */
94 iterpool = svn_pool_create(pool);
95 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
97 const char *key = apr_hash_this_key(hi);
98 svn_string_t *val = apr_hash_this_val(hi);
100 svn_pool_clear(iterpool);
102 if (strncmp(key, SVN_PROP_TXN_PREFIX,
103 (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
104 svn_error_clear(svn_fs_change_txn_prop(txn, key, val, iterpool));
106 svn_pool_destroy(iterpool);
111 /* Run post-commit hooks. */
112 if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env,
113 *new_rev, txn_name, pool)))
115 err2 = svn_error_create
116 (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2,
117 _("Commit succeeded, but post-commit hook failed"));
120 return svn_error_compose_create(err, err2);
125 /*** Transaction creation wrappers. ***/
129 svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
132 apr_hash_t *revprop_table,
135 apr_array_header_t *revprops;
136 const char *txn_name;
137 svn_string_t *author = svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR);
138 apr_hash_t *hooks_env;
142 /* Parse the hooks-env file (if any). */
143 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
146 /* Begin the transaction, ask for the fs to do on-the-fly lock checks.
147 We fetch its name, too, so the start-commit hook can use it. */
148 SVN_ERR(svn_fs_begin_txn2(&txn, repos->fs, rev,
149 SVN_FS_TXN_CHECK_LOCKS, pool));
150 err = svn_fs_txn_name(&txn_name, txn, pool);
152 return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
154 /* We pass the revision properties to the filesystem by adding them
155 as properties on the txn. Later, when we commit the txn, these
156 properties will be copied into the newly created revision. */
157 revprops = svn_prop_hash_to_array(revprop_table, pool);
158 err = svn_repos_fs_change_txn_props(txn, revprops, pool);
160 return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
162 /* Run start-commit hooks. */
163 err = svn_repos__hooks_start_commit(repos, hooks_env,
164 author ? author->data : NULL,
165 repos->client_capabilities, txn_name,
168 return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
170 /* We have API promise that *TXN_P is unaffected on failure. */
177 svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p,
184 apr_hash_t *revprop_table = apr_hash_make(pool);
186 svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
187 svn_string_create(author, pool));
189 svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
190 svn_string_create(log_msg, pool));
191 return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, revprop_table,
196 /*** Property wrappers ***/
199 svn_repos__validate_prop(const char *name,
200 const svn_string_t *value,
203 svn_prop_kind_t kind = svn_property_kind2(name);
205 /* Allow deleting any property, even a property we don't allow to set. */
209 /* Disallow setting non-regular properties. */
210 if (kind != svn_prop_regular_kind)
211 return svn_error_createf
212 (SVN_ERR_REPOS_BAD_ARGS, NULL,
213 _("Storage of non-regular property '%s' is disallowed through the "
214 "repository interface, and could indicate a bug in your client"),
217 /* Validate "svn:" properties. */
218 if (svn_prop_is_svn_prop(name) && value != NULL)
220 /* Validate that translated props (e.g., svn:log) are UTF-8 with
221 * LF line endings. */
222 if (svn_prop_needs_translation(name))
224 if (!svn_utf__is_valid(value->data, value->len))
226 return svn_error_createf
227 (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
228 _("Cannot accept '%s' property because it is not encoded in "
232 /* Disallow inconsistent line ending style, by simply looking for
233 * carriage return characters ('\r'). */
234 if (strchr(value->data, '\r') != NULL)
236 return svn_error_createf
237 (SVN_ERR_BAD_PROPERTY_VALUE, NULL,
238 _("Cannot accept non-LF line endings in '%s' property"),
243 /* "svn:date" should be a valid date. */
244 if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
249 err = svn_time_from_cstring(&temp, value->data, pool);
251 return svn_error_create(SVN_ERR_BAD_PROPERTY_VALUE,
260 /* Verify the mergeinfo property value VALUE and return an error if it
261 * is invalid. The PATH on which that property is set is used for error
262 * messages only. Use SCRATCH_POOL for temporary allocations. */
264 verify_mergeinfo(const svn_string_t *value,
266 apr_pool_t *scratch_pool)
269 svn_mergeinfo_t mergeinfo;
271 /* It's okay to delete svn:mergeinfo. */
275 /* Mergeinfo is UTF-8 encoded so the number of bytes returned by strlen()
276 * should match VALUE->LEN. Prevents trailing garbage in the property. */
277 if (strlen(value->data) != value->len)
278 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
279 _("Commit rejected because mergeinfo on '%s' "
280 "contains unexpected string terminator"),
283 err = svn_mergeinfo_parse(&mergeinfo, value->data, scratch_pool);
285 return svn_error_createf(err->apr_err, err,
286 _("Commit rejected because mergeinfo on '%s' "
287 "is syntactically invalid"),
294 svn_repos_fs_change_node_prop(svn_fs_root_t *root,
297 const svn_string_t *value,
300 if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
301 SVN_ERR(verify_mergeinfo(value, path, pool));
303 /* Validate the property, then call the wrapped function. */
304 SVN_ERR(svn_repos__validate_prop(name, value, pool));
305 return svn_fs_change_node_prop(root, path, name, value, pool);
310 svn_repos_fs_change_txn_props(svn_fs_txn_t *txn,
311 const apr_array_header_t *txnprops,
316 for (i = 0; i < txnprops->nelts; i++)
318 svn_prop_t *prop = &APR_ARRAY_IDX(txnprops, i, svn_prop_t);
319 SVN_ERR(svn_repos__validate_prop(prop->name, prop->value, pool));
322 return svn_fs_change_txn_props(txn, txnprops, pool);
327 svn_repos_fs_change_txn_prop(svn_fs_txn_t *txn,
329 const svn_string_t *value,
332 apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t));
337 APR_ARRAY_PUSH(props, svn_prop_t) = prop;
339 return svn_repos_fs_change_txn_props(txn, props, pool);
344 svn_repos_fs_change_rev_prop4(svn_repos_t *repos,
348 const svn_string_t *const *old_value_p,
349 const svn_string_t *new_value,
350 svn_boolean_t use_pre_revprop_change_hook,
351 svn_boolean_t use_post_revprop_change_hook,
352 svn_repos_authz_func_t authz_read_func,
353 void *authz_read_baton,
356 svn_repos_revision_access_level_t readability;
358 SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
359 authz_read_func, authz_read_baton,
362 if (readability == svn_repos_revision_access_full)
364 const svn_string_t *old_value;
366 apr_hash_t *hooks_env;
368 SVN_ERR(svn_repos__validate_prop(name, new_value, pool));
370 /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */
373 old_value = *old_value_p;
377 /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments
378 * to the hooks to be accurate. */
379 svn_string_t *old_value2;
381 SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool));
382 old_value = old_value2;
385 /* Prepare ACTION. */
388 else if (! old_value)
393 /* Parse the hooks-env file (if any, and if to be used). */
394 if (use_pre_revprop_change_hook || use_post_revprop_change_hook)
395 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
398 /* ### currently not passing the old_value to hooks */
399 if (use_pre_revprop_change_hook)
400 SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev,
401 author, name, new_value,
404 SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name,
405 &old_value, new_value, pool));
407 if (use_post_revprop_change_hook)
408 SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev,
409 author, name, old_value,
412 else /* rev is either unreadable or only partially readable */
414 return svn_error_createf
415 (SVN_ERR_AUTHZ_UNREADABLE, NULL,
416 _("Write denied: not authorized to read all of revision %ld"), rev);
424 svn_repos_fs_revision_prop(svn_string_t **value_p,
427 const char *propname,
428 svn_repos_authz_func_t authz_read_func,
429 void *authz_read_baton,
432 svn_repos_revision_access_level_t readability;
434 SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
435 authz_read_func, authz_read_baton,
438 if (readability == svn_repos_revision_access_none)
440 /* Property? What property? */
443 else if (readability == svn_repos_revision_access_partial)
445 /* Only svn:author and svn:date are fetchable. */
446 if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) != 0)
447 && (strcmp(propname, SVN_PROP_REVISION_DATE) != 0))
451 SVN_ERR(svn_fs_revision_prop(value_p, repos->fs,
452 rev, propname, pool));
454 else /* wholly readable revision */
456 SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool));
465 svn_repos_fs_revision_proplist(apr_hash_t **table_p,
468 svn_repos_authz_func_t authz_read_func,
469 void *authz_read_baton,
472 svn_repos_revision_access_level_t readability;
474 SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
475 authz_read_func, authz_read_baton,
478 if (readability == svn_repos_revision_access_none)
480 /* Return an empty hash. */
481 *table_p = apr_hash_make(pool);
483 else if (readability == svn_repos_revision_access_partial)
488 /* Produce two property hashtables, both in POOL. */
489 SVN_ERR(svn_fs_revision_proplist(&tmphash, repos->fs, rev, pool));
490 *table_p = apr_hash_make(pool);
492 /* If they exist, we only copy svn:author and svn:date into the
493 'real' hashtable being returned. */
494 value = svn_hash_gets(tmphash, SVN_PROP_REVISION_AUTHOR);
496 svn_hash_sets(*table_p, SVN_PROP_REVISION_AUTHOR, value);
498 value = svn_hash_gets(tmphash, SVN_PROP_REVISION_DATE);
500 svn_hash_sets(*table_p, SVN_PROP_REVISION_DATE, value);
502 else /* wholly readable revision */
504 SVN_ERR(svn_fs_revision_proplist(table_p, repos->fs, rev, pool));
510 struct lock_many_baton_t {
511 svn_boolean_t need_lock;
512 apr_array_header_t *paths;
513 svn_fs_lock_callback_t lock_callback;
519 /* Implements svn_fs_lock_callback_t. Used by svn_repos_fs_lock_many
520 and svn_repos_fs_unlock_many to record the paths for use by post-
521 hooks, forward to the supplied callback and record any callback
524 lock_many_cb(void *lock_baton,
526 const svn_lock_t *lock,
530 struct lock_many_baton_t *b = lock_baton;
532 if (!b->cb_err && b->lock_callback)
533 b->cb_err = b->lock_callback(b->lock_baton, path, lock, fs_err, pool);
535 if ((b->need_lock && lock) || (!b->need_lock && !fs_err))
536 APR_ARRAY_PUSH(b->paths, const char *) = apr_pstrdup(b->pool, path);
542 svn_repos_fs_lock_many(svn_repos_t *repos,
545 svn_boolean_t is_dav_comment,
546 apr_time_t expiration_date,
547 svn_boolean_t steal_lock,
548 svn_fs_lock_callback_t lock_callback,
550 apr_pool_t *result_pool,
551 apr_pool_t *scratch_pool)
553 svn_error_t *err, *cb_err = SVN_NO_ERROR;
554 svn_fs_access_t *access_ctx = NULL;
555 const char *username = NULL;
556 apr_hash_t *hooks_env;
557 apr_hash_t *pre_targets = apr_hash_make(scratch_pool);
558 apr_hash_index_t *hi;
559 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
560 struct lock_many_baton_t baton;
562 if (!apr_hash_count(targets))
565 /* Parse the hooks-env file (if any). */
566 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
567 scratch_pool, scratch_pool));
569 SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
571 SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
574 return svn_error_create
575 (SVN_ERR_FS_NO_USER, NULL,
576 "Cannot lock path, no authenticated username available.");
578 /* Run pre-lock hook. This could throw error, preventing
579 svn_fs_lock2() from happening for that path. */
580 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
582 const char *new_token;
583 svn_fs_lock_target_t *target;
584 const char *path = apr_hash_this_key(hi);
586 svn_pool_clear(iterpool);
588 err = svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
589 username, comment, steal_lock, iterpool);
592 if (!cb_err && lock_callback)
593 cb_err = lock_callback(lock_baton, path, NULL, err, iterpool);
594 svn_error_clear(err);
599 target = apr_hash_this_val(hi);
601 svn_fs_lock_target_set_token(target, new_token);
602 svn_hash_sets(pre_targets, path, target);
605 if (!apr_hash_count(pre_targets))
606 return svn_error_trace(cb_err);
608 baton.need_lock = TRUE;
609 baton.paths = apr_array_make(scratch_pool, apr_hash_count(pre_targets),
610 sizeof(const char *));
611 baton.lock_callback = lock_callback;
612 baton.lock_baton = lock_baton;
613 baton.cb_err = cb_err;
614 baton.pool = scratch_pool;
616 err = svn_fs_lock_many(repos->fs, pre_targets, comment,
617 is_dav_comment, expiration_date, steal_lock,
618 lock_many_cb, &baton, result_pool, iterpool);
620 /* If there are locks run the post-lock even if there is an error. */
621 if (baton.paths->nelts)
623 svn_error_t *perr = svn_repos__hooks_post_lock(repos, hooks_env,
624 baton.paths, username,
628 perr = svn_error_create(SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, perr,
629 _("Locking succeeded, but post-lock hook failed"));
630 err = svn_error_compose_create(err, perr);
634 svn_pool_destroy(iterpool);
637 svn_error_compose(err, cb_err);
641 return svn_error_trace(err);
644 struct lock_baton_t {
645 const svn_lock_t *lock;
649 /* Implements svn_fs_lock_callback_t. Used by svn_repos_fs_lock and
650 svn_repos_fs_unlock to record the lock and error from
651 svn_repos_fs_lock_many and svn_repos_fs_unlock_many. */
653 lock_cb(void *lock_baton,
655 const svn_lock_t *lock,
659 struct lock_baton_t *b = lock_baton;
662 b->fs_err = svn_error_dup(fs_err);
668 svn_repos_fs_lock(svn_lock_t **lock,
673 svn_boolean_t is_dav_comment,
674 apr_time_t expiration_date,
675 svn_revnum_t current_rev,
676 svn_boolean_t steal_lock,
679 apr_hash_t *targets = apr_hash_make(pool);
680 svn_fs_lock_target_t *target = svn_fs_lock_target_create(token, current_rev,
683 struct lock_baton_t baton = {0};
685 svn_hash_sets(targets, path, target);
687 err = svn_repos_fs_lock_many(repos, targets, comment, is_dav_comment,
688 expiration_date, steal_lock, lock_cb, &baton,
692 *lock = (svn_lock_t*)baton.lock;
694 if (err && baton.fs_err)
695 svn_error_compose(err, baton.fs_err);
699 return svn_error_trace(err);
704 svn_repos_fs_unlock_many(svn_repos_t *repos,
706 svn_boolean_t break_lock,
707 svn_fs_lock_callback_t lock_callback,
709 apr_pool_t *result_pool,
710 apr_pool_t *scratch_pool)
712 svn_error_t *err, *cb_err = SVN_NO_ERROR;
713 svn_fs_access_t *access_ctx = NULL;
714 const char *username = NULL;
715 apr_hash_t *hooks_env;
716 apr_hash_t *pre_targets = apr_hash_make(scratch_pool);
717 apr_hash_index_t *hi;
718 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
719 struct lock_many_baton_t baton;
721 if (!apr_hash_count(targets))
724 /* Parse the hooks-env file (if any). */
725 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
726 scratch_pool, scratch_pool));
728 SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
730 SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
732 if (! break_lock && ! username)
733 return svn_error_create
734 (SVN_ERR_FS_NO_USER, NULL,
735 _("Cannot unlock, no authenticated username available"));
737 /* Run pre-unlock hook. This could throw error, preventing
738 svn_fs_unlock_many() from happening for that path. */
739 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
741 const char *path = apr_hash_this_key(hi);
742 const char *token = apr_hash_this_val(hi);
744 svn_pool_clear(iterpool);
746 err = svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
747 break_lock, iterpool);
750 if (!cb_err && lock_callback)
751 cb_err = lock_callback(lock_baton, path, NULL, err, iterpool);
752 svn_error_clear(err);
757 svn_hash_sets(pre_targets, path, token);
760 if (!apr_hash_count(pre_targets))
761 return svn_error_trace(cb_err);
763 baton.need_lock = FALSE;
764 baton.paths = apr_array_make(scratch_pool, apr_hash_count(pre_targets),
765 sizeof(const char *));
766 baton.lock_callback = lock_callback;
767 baton.lock_baton = lock_baton;
768 baton.cb_err = cb_err;
769 baton.pool = scratch_pool;
771 err = svn_fs_unlock_many(repos->fs, pre_targets, break_lock,
772 lock_many_cb, &baton, result_pool, iterpool);
774 /* If there are 'unlocks' run the post-unlock even if there is an error. */
775 if (baton.paths->nelts)
777 svn_error_t *perr = svn_repos__hooks_post_unlock(repos, hooks_env,
782 perr = svn_error_create(SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, perr,
783 _("Unlock succeeded, but post-unlock hook failed"));
784 err = svn_error_compose_create(err, perr);
788 svn_pool_destroy(iterpool);
791 svn_error_compose(err, cb_err);
795 return svn_error_trace(err);
799 svn_repos_fs_unlock(svn_repos_t *repos,
802 svn_boolean_t break_lock,
805 apr_hash_t *targets = apr_hash_make(pool);
807 struct lock_baton_t baton = {0};
812 svn_hash_sets(targets, path, token);
814 err = svn_repos_fs_unlock_many(repos, targets, break_lock, lock_cb, &baton,
817 if (err && baton.fs_err)
818 svn_error_compose(err, baton.fs_err);
822 return svn_error_trace(err);
826 struct get_locks_baton_t
829 svn_fs_root_t *head_root;
830 svn_repos_authz_func_t authz_read_func;
831 void *authz_read_baton;
836 /* This implements the svn_fs_get_locks_callback_t interface. */
838 get_locks_callback(void *baton,
842 struct get_locks_baton_t *b = baton;
843 svn_boolean_t readable = TRUE;
844 apr_pool_t *hash_pool = apr_hash_pool_get(b->locks);
846 /* If there's auth to deal with, deal with it. */
847 if (b->authz_read_func)
848 SVN_ERR(b->authz_read_func(&readable, b->head_root, lock->path,
849 b->authz_read_baton, pool));
851 /* If we can read this lock path, add the lock to the return hash. */
853 svn_hash_sets(b->locks, apr_pstrdup(hash_pool, lock->path),
854 svn_lock_dup(lock, hash_pool));
861 svn_repos_fs_get_locks2(apr_hash_t **locks,
865 svn_repos_authz_func_t authz_read_func,
866 void *authz_read_baton,
869 apr_hash_t *all_locks = apr_hash_make(pool);
870 svn_revnum_t head_rev;
871 struct get_locks_baton_t baton;
873 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
874 (depth == svn_depth_files) ||
875 (depth == svn_depth_immediates) ||
876 (depth == svn_depth_infinity));
878 SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool));
880 /* Populate our callback baton. */
881 baton.fs = repos->fs;
882 baton.locks = all_locks;
883 baton.authz_read_func = authz_read_func;
884 baton.authz_read_baton = authz_read_baton;
885 SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs,
888 /* Get all the locks. */
889 SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth,
890 get_locks_callback, &baton, pool));
892 *locks = baton.locks;
898 svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo,
900 const apr_array_header_t *paths,
902 svn_mergeinfo_inheritance_t inherit,
903 svn_boolean_t include_descendants,
904 svn_repos_authz_func_t authz_read_func,
905 void *authz_read_baton,
908 /* Here we cast away 'const', but won't try to write through this pointer
909 * without first allocating a new array. */
910 apr_array_header_t *readable_paths = (apr_array_header_t *) paths;
912 apr_pool_t *iterpool = svn_pool_create(pool);
914 if (!SVN_IS_VALID_REVNUM(rev))
915 SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool));
916 SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool));
918 /* Filter out unreadable paths before divining merge tracking info. */
923 for (i = 0; i < paths->nelts; i++)
925 svn_boolean_t readable;
926 const char *path = APR_ARRAY_IDX(paths, i, char *);
927 svn_pool_clear(iterpool);
928 SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton,
930 if (readable && readable_paths != paths)
931 APR_ARRAY_PUSH(readable_paths, const char *) = path;
932 else if (!readable && readable_paths == paths)
934 /* Requested paths differ from readable paths. Fork
935 list of readable paths from requested paths. */
937 readable_paths = apr_array_make(pool, paths->nelts - 1,
939 for (j = 0; j < i; j++)
941 path = APR_ARRAY_IDX(paths, j, char *);
942 APR_ARRAY_PUSH(readable_paths, const char *) = path;
948 /* We consciously do not perform authz checks on the paths returned
949 in *MERGEINFO, avoiding massive authz overhead which would allow
950 us to protect the name of where a change was merged from, but not
951 the change itself. */
952 /* ### TODO(reint): ... but how about descendant merged-to paths? */
953 if (readable_paths->nelts > 0)
954 SVN_ERR(svn_fs_get_mergeinfo2(mergeinfo, root, readable_paths, inherit,
955 include_descendants, TRUE, pool, pool));
957 *mergeinfo = apr_hash_make(pool);
959 svn_pool_destroy(iterpool);
963 struct pack_notify_baton
965 svn_repos_notify_func_t notify_func;
969 /* Implements svn_fs_pack_notify_t. */
971 pack_notify_func(void *baton,
973 svn_fs_pack_notify_action_t pack_action,
976 struct pack_notify_baton *pnb = baton;
977 svn_repos_notify_t *notify;
979 /* Simple conversion works for these values. */
980 SVN_ERR_ASSERT(pack_action >= svn_fs_pack_notify_start
981 && pack_action <= svn_fs_pack_notify_end_revprop);
983 notify = svn_repos_notify_create(pack_action
984 + svn_repos_notify_pack_shard_start
985 - svn_fs_pack_notify_start,
987 notify->shard = shard;
988 pnb->notify_func(pnb->notify_baton, notify, pool);
994 svn_repos_fs_pack2(svn_repos_t *repos,
995 svn_repos_notify_func_t notify_func,
997 svn_cancel_func_t cancel_func,
1001 struct pack_notify_baton pnb;
1003 pnb.notify_func = notify_func;
1004 pnb.notify_baton = notify_baton;
1006 return svn_fs_pack(repos->db_path,
1007 notify_func ? pack_notify_func : NULL,
1008 notify_func ? &pnb : NULL,
1009 cancel_func, cancel_baton, pool);
1013 svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p,
1014 svn_fs_root_t *root,
1016 const char *propname,
1017 svn_repos_authz_func_t authz_read_func,
1018 void *authz_read_baton,
1019 apr_pool_t *result_pool,
1020 apr_pool_t *scratch_pool)
1022 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1023 apr_array_header_t *inherited_props;
1024 const char *parent_path = path;
1026 inherited_props = apr_array_make(result_pool, 1,
1027 sizeof(svn_prop_inherited_item_t *));
1028 while (!(parent_path[0] == '/' && parent_path[1] == '\0'))
1030 svn_boolean_t allowed = TRUE;
1031 apr_hash_t *parent_properties = NULL;
1033 svn_pool_clear(iterpool);
1034 parent_path = svn_fspath__dirname(parent_path, scratch_pool);
1036 if (authz_read_func)
1037 SVN_ERR(authz_read_func(&allowed, root, parent_path,
1038 authz_read_baton, iterpool));
1043 svn_string_t *propval;
1045 SVN_ERR(svn_fs_node_prop(&propval, root, parent_path, propname,
1049 parent_properties = apr_hash_make(result_pool);
1050 svn_hash_sets(parent_properties, propname, propval);
1055 SVN_ERR(svn_fs_node_proplist(&parent_properties, root,
1056 parent_path, result_pool));
1059 if (parent_properties && apr_hash_count(parent_properties))
1061 svn_prop_inherited_item_t *i_props =
1062 apr_pcalloc(result_pool, sizeof(*i_props));
1063 i_props->path_or_url =
1064 apr_pstrdup(result_pool, parent_path + 1);
1065 i_props->prop_hash = parent_properties;
1066 /* Build the output array in depth-first order. */
1067 svn_sort__array_insert(inherited_props, &i_props, 0);
1072 svn_pool_destroy(iterpool);
1074 *inherited_props_p = inherited_props;
1075 return SVN_NO_ERROR;
1079 * vim:ts=4:sw=2:expandtab:tw=80:fo=tcroq
1080 * vim:isk=a-z,A-Z,48-57,_,.,-,>
1081 * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0