1 /* fs-wrap.c --- filesystem interface wrappers.
3 * ====================================================================
4 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
22 #include "svn_pools.h"
23 #include "svn_error.h"
26 #include "svn_props.h"
27 #include "svn_repos.h"
29 #include "svn_private_config.h"
32 /*** Commit wrappers ***/
35 svn_repos_fs_commit_txn(const char **conflict_p
,
37 svn_revnum_t
*new_rev
,
44 /* Run pre-commit hooks. */
45 SVN_ERR(svn_fs_txn_name(&txn_name
, txn
, pool
));
46 SVN_ERR(svn_repos__hooks_pre_commit(repos
, txn_name
, pool
));
49 SVN_ERR(svn_fs_commit_txn(conflict_p
, new_rev
, txn
, pool
));
51 /* Run post-commit hooks. Notice that we're wrapping the error
52 with a -specific- errorcode, so that our caller knows not to try
53 and abort the transaction. */
54 if ((err
= svn_repos__hooks_post_commit(repos
, *new_rev
, pool
)))
55 return svn_error_create
56 (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED
, err
,
57 _("Commit succeeded, but post-commit hook failed"));
64 /*** Transaction creation wrappers. ***/
68 svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t
**txn_p
,
71 apr_hash_t
*revprop_table
,
74 svn_string_t
*author
= apr_hash_get(revprop_table
, SVN_PROP_REVISION_AUTHOR
,
76 apr_array_header_t
*revprops
;
78 /* Run start-commit hooks. */
79 SVN_ERR(svn_repos__hooks_start_commit(repos
, author
? author
->data
: NULL
,
80 repos
->client_capabilities
, pool
));
82 /* Begin the transaction, ask for the fs to do on-the-fly lock checks. */
83 SVN_ERR(svn_fs_begin_txn2(txn_p
, repos
->fs
, rev
,
84 SVN_FS_TXN_CHECK_LOCKS
, pool
));
86 /* We pass the revision properties to the filesystem by adding them
87 as properties on the txn. Later, when we commit the txn, these
88 properties will be copied into the newly created revision. */
89 revprops
= svn_prop_hash_to_array(revprop_table
, pool
);
90 return svn_repos_fs_change_txn_props(*txn_p
, revprops
, pool
);
95 svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t
**txn_p
,
102 apr_hash_t
*revprop_table
= apr_hash_make(pool
);
104 apr_hash_set(revprop_table
, SVN_PROP_REVISION_AUTHOR
,
106 svn_string_create(author
, pool
));
108 apr_hash_set(revprop_table
, SVN_PROP_REVISION_LOG
,
110 svn_string_create(log_msg
, pool
));
111 return svn_repos_fs_begin_txn_for_commit2(txn_p
, repos
, rev
, revprop_table
,
117 svn_repos_fs_begin_txn_for_update(svn_fs_txn_t
**txn_p
,
123 /* ### someday, we might run a read-hook here. */
125 /* Begin the transaction. */
126 SVN_ERR(svn_fs_begin_txn2(txn_p
, repos
->fs
, rev
, 0, pool
));
128 /* We pass the author to the filesystem by adding it as a property
136 val
.len
= strlen(author
);
137 SVN_ERR(svn_fs_change_txn_prop(*txn_p
, SVN_PROP_REVISION_AUTHOR
,
146 /*** Property wrappers ***/
148 /* Validate that property NAME is valid for use in a Subversion
151 validate_prop(const char *name
)
153 svn_prop_kind_t kind
= svn_property_kind(NULL
, name
);
154 if (kind
!= svn_prop_regular_kind
)
155 return svn_error_createf
156 (SVN_ERR_REPOS_BAD_ARGS
, NULL
,
157 _("Storage of non-regular property '%s' is disallowed through the "
158 "repository interface, and could indicate a bug in your client"),
165 svn_repos_fs_change_node_prop(svn_fs_root_t
*root
,
168 const svn_string_t
*value
,
171 /* Validate the property, then call the wrapped function. */
172 SVN_ERR(validate_prop(name
));
173 return svn_fs_change_node_prop(root
, path
, name
, value
, pool
);
178 svn_repos_fs_change_txn_props(svn_fs_txn_t
*txn
,
179 apr_array_header_t
*txnprops
,
184 for (i
= 0; i
< txnprops
->nelts
; i
++)
185 SVN_ERR(validate_prop(APR_ARRAY_IDX(txnprops
, i
, svn_prop_t
).name
));
187 return svn_fs_change_txn_props(txn
, txnprops
, pool
);
192 svn_repos_fs_change_txn_prop(svn_fs_txn_t
*txn
,
194 const svn_string_t
*value
,
197 apr_array_header_t
*props
= apr_array_make(pool
, 1, sizeof(svn_prop_t
));
202 APR_ARRAY_PUSH(props
, svn_prop_t
) = prop
;
204 return svn_repos_fs_change_txn_props(txn
, props
, pool
);
209 svn_repos_fs_change_rev_prop3(svn_repos_t
*repos
,
213 const svn_string_t
*new_value
,
214 svn_boolean_t use_pre_revprop_change_hook
,
215 svn_boolean_t use_post_revprop_change_hook
,
216 svn_repos_authz_func_t authz_read_func
,
217 void *authz_read_baton
,
220 svn_string_t
*old_value
;
221 svn_repos_revision_access_level_t readability
;
224 SVN_ERR(svn_repos_check_revision_access(&readability
, repos
, rev
,
225 authz_read_func
, authz_read_baton
,
228 if (readability
== svn_repos_revision_access_full
)
230 SVN_ERR(validate_prop(name
));
231 SVN_ERR(svn_fs_revision_prop(&old_value
, repos
->fs
, rev
, name
, pool
));
234 else if (! old_value
)
239 if (use_pre_revprop_change_hook
)
240 SVN_ERR(svn_repos__hooks_pre_revprop_change(repos
, rev
, author
, name
,
241 new_value
, action
, pool
));
243 SVN_ERR(svn_fs_change_rev_prop(repos
->fs
, rev
, name
, new_value
, pool
));
245 if (use_post_revprop_change_hook
)
246 SVN_ERR(svn_repos__hooks_post_revprop_change(repos
, rev
, author
, name
,
247 old_value
, action
, pool
));
249 else /* rev is either unreadable or only partially readable */
251 return svn_error_createf
252 (SVN_ERR_AUTHZ_UNREADABLE
, NULL
,
253 _("Write denied: not authorized to read all of revision %ld"), rev
);
261 svn_repos_fs_change_rev_prop2(svn_repos_t
*repos
,
265 const svn_string_t
*new_value
,
266 svn_repos_authz_func_t authz_read_func
,
267 void *authz_read_baton
,
270 return svn_repos_fs_change_rev_prop3(repos
, rev
, author
, name
, new_value
,
271 TRUE
, TRUE
, authz_read_func
,
272 authz_read_baton
, pool
);
278 svn_repos_fs_change_rev_prop(svn_repos_t
*repos
,
282 const svn_string_t
*new_value
,
285 return svn_repos_fs_change_rev_prop2(repos
, rev
, author
, name
, new_value
,
292 svn_repos_fs_revision_prop(svn_string_t
**value_p
,
295 const char *propname
,
296 svn_repos_authz_func_t authz_read_func
,
297 void *authz_read_baton
,
300 svn_repos_revision_access_level_t readability
;
302 SVN_ERR(svn_repos_check_revision_access(&readability
, repos
, rev
,
303 authz_read_func
, authz_read_baton
,
306 if (readability
== svn_repos_revision_access_none
)
308 /* Property? What property? */
311 else if (readability
== svn_repos_revision_access_partial
)
313 /* Only svn:author and svn:date are fetchable. */
314 if ((strncmp(propname
, SVN_PROP_REVISION_AUTHOR
,
315 strlen(SVN_PROP_REVISION_AUTHOR
)) != 0)
316 && (strncmp(propname
, SVN_PROP_REVISION_DATE
,
317 strlen(SVN_PROP_REVISION_DATE
)) != 0))
321 SVN_ERR(svn_fs_revision_prop(value_p
, repos
->fs
,
322 rev
, propname
, pool
));
324 else /* wholly readable revision */
326 SVN_ERR(svn_fs_revision_prop(value_p
, repos
->fs
, rev
, propname
, pool
));
335 svn_repos_fs_revision_proplist(apr_hash_t
**table_p
,
338 svn_repos_authz_func_t authz_read_func
,
339 void *authz_read_baton
,
342 svn_repos_revision_access_level_t readability
;
344 SVN_ERR(svn_repos_check_revision_access(&readability
, repos
, rev
,
345 authz_read_func
, authz_read_baton
,
348 if (readability
== svn_repos_revision_access_none
)
350 /* Return an empty hash. */
351 *table_p
= apr_hash_make(pool
);
353 else if (readability
== svn_repos_revision_access_partial
)
358 /* Produce two property hashtables, both in POOL. */
359 SVN_ERR(svn_fs_revision_proplist(&tmphash
, repos
->fs
, rev
, pool
));
360 *table_p
= apr_hash_make(pool
);
362 /* If they exist, we only copy svn:author and svn:date into the
363 'real' hashtable being returned. */
364 value
= apr_hash_get(tmphash
, SVN_PROP_REVISION_AUTHOR
,
365 APR_HASH_KEY_STRING
);
367 apr_hash_set(*table_p
, SVN_PROP_REVISION_AUTHOR
,
368 APR_HASH_KEY_STRING
, value
);
370 value
= apr_hash_get(tmphash
, SVN_PROP_REVISION_DATE
,
371 APR_HASH_KEY_STRING
);
373 apr_hash_set(*table_p
, SVN_PROP_REVISION_DATE
,
374 APR_HASH_KEY_STRING
, value
);
376 else /* wholly readable revision */
378 SVN_ERR(svn_fs_revision_proplist(table_p
, repos
->fs
, rev
, pool
));
385 svn_repos_fs_lock(svn_lock_t
**lock
,
390 svn_boolean_t is_dav_comment
,
391 apr_time_t expiration_date
,
392 svn_revnum_t current_rev
,
393 svn_boolean_t steal_lock
,
397 svn_fs_access_t
*access_ctx
= NULL
;
398 const char *username
= NULL
;
399 apr_array_header_t
*paths
;
401 /* Setup an array of paths in anticipation of the ra layers handling
402 multiple locks in one request (1.3 most likely). This is only
403 used by svn_repos__hooks_post_lock. */
404 paths
= apr_array_make(pool
, 1, sizeof(const char *));
405 APR_ARRAY_PUSH(paths
, const char *) = path
;
407 SVN_ERR(svn_fs_get_access(&access_ctx
, repos
->fs
));
409 SVN_ERR(svn_fs_access_get_username(&username
, access_ctx
));
412 return svn_error_createf
413 (SVN_ERR_FS_NO_USER
, NULL
,
414 "Cannot lock path '%s', no authenticated username available.", path
);
416 /* Run pre-lock hook. This could throw error, preventing
417 svn_fs_lock() from happening. */
418 SVN_ERR(svn_repos__hooks_pre_lock(repos
, path
, username
, pool
));
421 SVN_ERR(svn_fs_lock(lock
, repos
->fs
, path
, token
, comment
, is_dav_comment
,
422 expiration_date
, current_rev
, steal_lock
, pool
));
424 /* Run post-lock hook. */
425 if ((err
= svn_repos__hooks_post_lock(repos
, paths
, username
, pool
)))
426 return svn_error_create
427 (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED
, err
,
428 "Lock succeeded, but post-lock hook failed");
435 svn_repos_fs_unlock(svn_repos_t
*repos
,
438 svn_boolean_t break_lock
,
442 svn_fs_access_t
*access_ctx
= NULL
;
443 const char *username
= NULL
;
444 /* Setup an array of paths in anticipation of the ra layers handling
445 multiple locks in one request (1.3 most likely). This is only
446 used by svn_repos__hooks_post_lock. */
447 apr_array_header_t
*paths
= apr_array_make(pool
, 1, sizeof(const char *));
448 APR_ARRAY_PUSH(paths
, const char *) = path
;
450 SVN_ERR(svn_fs_get_access(&access_ctx
, repos
->fs
));
452 SVN_ERR(svn_fs_access_get_username(&username
, access_ctx
));
454 if (! break_lock
&& ! username
)
455 return svn_error_createf
456 (SVN_ERR_FS_NO_USER
, NULL
,
457 _("Cannot unlock path '%s', no authenticated username available"),
460 /* Run pre-unlock hook. This could throw error, preventing
461 svn_fs_unlock() from happening. */
462 SVN_ERR(svn_repos__hooks_pre_unlock(repos
, path
, username
, pool
));
465 SVN_ERR(svn_fs_unlock(repos
->fs
, path
, token
, break_lock
, pool
));
467 /* Run post-unlock hook. */
468 if ((err
= svn_repos__hooks_post_unlock(repos
, paths
, username
, pool
)))
469 return svn_error_create
470 (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED
, err
,
471 _("Unlock succeeded, but post-unlock hook failed"));
477 struct get_locks_baton_t
480 svn_fs_root_t
*head_root
;
481 svn_repos_authz_func_t authz_read_func
;
482 void *authz_read_baton
;
487 /* This implements the svn_fs_get_locks_callback_t interface. */
489 get_locks_callback(void *baton
,
493 struct get_locks_baton_t
*b
= baton
;
494 svn_boolean_t readable
= TRUE
;
495 apr_pool_t
*hash_pool
= apr_hash_pool_get(b
->locks
);
497 /* If there's auth to deal with, deal with it. */
498 if (b
->authz_read_func
)
500 SVN_ERR(b
->authz_read_func(&readable
, b
->head_root
, lock
->path
,
501 b
->authz_read_baton
, pool
));
504 /* If we can read this lock path, add the lock to the return hash. */
506 apr_hash_set(b
->locks
, apr_pstrdup(hash_pool
, lock
->path
),
507 APR_HASH_KEY_STRING
, svn_lock_dup(lock
, hash_pool
));
514 svn_repos_fs_get_locks(apr_hash_t
**locks
,
517 svn_repos_authz_func_t authz_read_func
,
518 void *authz_read_baton
,
521 apr_hash_t
*all_locks
= apr_hash_make(pool
);
522 svn_revnum_t head_rev
;
523 struct get_locks_baton_t baton
;
525 /* Locks are always said to apply to HEAD revision, so we'll check
526 to see if locked-paths are readable in HEAD as well. */
527 SVN_ERR(svn_fs_youngest_rev(&head_rev
, repos
->fs
, pool
));
529 /* Populate our callback baton. */
530 baton
.fs
= repos
->fs
;
531 baton
.locks
= all_locks
;
532 baton
.authz_read_func
= authz_read_func
;
533 baton
.authz_read_baton
= authz_read_baton
;
534 SVN_ERR(svn_fs_revision_root(&(baton
.head_root
), repos
->fs
,
537 /* Get all the locks. */
538 SVN_ERR(svn_fs_get_locks(repos
->fs
, path
, get_locks_callback
,
541 *locks
= baton
.locks
;
547 svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t
*mergeinfo
,
549 const apr_array_header_t
*paths
,
551 svn_mergeinfo_inheritance_t inherit
,
552 svn_boolean_t include_descendants
,
553 svn_repos_authz_func_t authz_read_func
,
554 void *authz_read_baton
,
557 apr_array_header_t
*readable_paths
= (apr_array_header_t
*) paths
;
559 apr_pool_t
*iterpool
= svn_pool_create(pool
);
562 if (!SVN_IS_VALID_REVNUM(rev
))
563 SVN_ERR(svn_fs_youngest_rev(&rev
, repos
->fs
, pool
));
564 SVN_ERR(svn_fs_revision_root(&root
, repos
->fs
, rev
, pool
));
566 /* Filter out unreadable paths before divining merge tracking info. */
569 for (i
= 0; i
< paths
->nelts
; i
++)
571 svn_boolean_t readable
;
572 const char *path
= APR_ARRAY_IDX(paths
, i
, char *);
573 svn_pool_clear(iterpool
);
574 SVN_ERR(authz_read_func(&readable
, root
, path
, authz_read_baton
,
576 if (readable
&& readable_paths
!= paths
)
577 APR_ARRAY_PUSH(readable_paths
, const char *) = path
;
578 else if (!readable
&& readable_paths
== paths
)
580 /* Requested paths differ from readable paths. Fork
581 list of readable paths from requested paths. */
583 readable_paths
= apr_array_make(pool
, paths
->nelts
- 1,
585 for (j
= 0; j
< i
; j
++)
587 path
= APR_ARRAY_IDX(paths
, j
, char *);
588 APR_ARRAY_PUSH(readable_paths
, const char *) = path
;
594 /* We consciously do not perform authz checks on the paths returned
595 in *MERGEINFO, avoiding massive authz overhead which would allow
596 us to protect the name of where a change was merged from, but not
597 the change itself. */
598 /* ### TODO(reint): ... but how about descendant merged-to paths? */
599 if (readable_paths
->nelts
> 0)
600 SVN_ERR(svn_fs_get_mergeinfo(mergeinfo
, root
, readable_paths
, inherit
,
601 include_descendants
, pool
));
603 *mergeinfo
= apr_hash_make(pool
);
605 svn_pool_destroy(iterpool
);
612 * vim:ts=4:sw=4:expandtab:tw=80:fo=tcroq
613 * vim:isk=a-z,A-Z,48-57,_,.,-,>
614 * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0