1 /* lock.c : functions for manipulating filesystem locks.
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
24 #include "svn_hash.h"
25 #include "svn_pools.h"
26 #include "svn_error.h"
27 #include "svn_fs.h"
28 #include "svn_private_config.h"
29
30 #include <apr_uuid.h>
31
32 #include "lock.h"
33 #include "tree.h"
34 #include "err.h"
35 #include "bdb/locks-table.h"
36 #include "bdb/lock-tokens-table.h"
37 #include "util/fs_skels.h"
38 #include "../libsvn_fs/fs-loader.h"
39 #include "private/svn_fs_util.h"
40 #include "private/svn_subr_private.h"
41 #include "private/svn_dep_compat.h"
42 #include "revs-txns.h"
43
44
45 /* Add LOCK and its associated LOCK_TOKEN (associated with PATH) as
46 part of TRAIL. */
47 static svn_error_t *
add_lock_and_token(svn_lock_t * lock,const char * lock_token,const char * path,trail_t * trail)48 add_lock_and_token(svn_lock_t *lock,
49 const char *lock_token,
50 const char *path,
51 trail_t *trail)
52 {
53 SVN_ERR(svn_fs_bdb__lock_add(trail->fs, lock_token, lock,
54 trail, trail->pool));
55 return svn_fs_bdb__lock_token_add(trail->fs, path, lock_token,
56 trail, trail->pool);
57 }
58
59
60 /* Delete LOCK_TOKEN and its corresponding lock (associated with PATH,
61 whose KIND is supplied), as part of TRAIL. */
62 static svn_error_t *
delete_lock_and_token(const char * lock_token,const char * path,trail_t * trail)63 delete_lock_and_token(const char *lock_token,
64 const char *path,
65 trail_t *trail)
66 {
67 SVN_ERR(svn_fs_bdb__lock_delete(trail->fs, lock_token,
68 trail, trail->pool));
69 return svn_fs_bdb__lock_token_delete(trail->fs, path,
70 trail, trail->pool);
71 }
72
73
74 /* The effective arguments for txn_body_lock() below. */
75 struct lock_args
76 {
77 svn_lock_t **lock_p;
78 const char *path;
79 const char *token;
80 const char *comment;
81 svn_boolean_t is_dav_comment;
82 svn_boolean_t steal_lock;
83 apr_time_t expiration_date;
84 svn_revnum_t current_rev;
85 apr_pool_t *result_pool;
86 };
87
88
89 /* The body of svn_fs_base__lock(), which see.
90
91 BATON is a 'struct lock_args *' holding the effective arguments.
92 BATON->path is the canonical abspath to lock. Set *BATON->lock_p
93 to the resulting lock. For the other arguments, see
94 svn_fs_lock_many().
95
96 This implements the svn_fs_base__retry_txn() 'body' callback type.
97 */
98 static svn_error_t *
txn_body_lock(void * baton,trail_t * trail)99 txn_body_lock(void *baton, trail_t *trail)
100 {
101 struct lock_args *args = baton;
102 svn_node_kind_t kind = svn_node_file;
103 svn_lock_t *existing_lock;
104 svn_lock_t *lock;
105
106 *args->lock_p = NULL;
107
108 SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool));
109
110 /* Until we implement directory locks someday, we only allow locks
111 on files. */
112 if (kind == svn_node_dir)
113 return SVN_FS__ERR_NOT_FILE(trail->fs, args->path);
114
115 /* While our locking implementation easily supports the locking of
116 nonexistent paths, we deliberately choose not to allow such madness. */
117 if (kind == svn_node_none)
118 {
119 if (SVN_IS_VALID_REVNUM(args->current_rev))
120 return svn_error_createf(
121 SVN_ERR_FS_OUT_OF_DATE, NULL,
122 _("Path '%s' doesn't exist in HEAD revision"),
123 args->path);
124 else
125 return svn_error_createf(
126 SVN_ERR_FS_NOT_FOUND, NULL,
127 _("Path '%s' doesn't exist in HEAD revision"),
128 args->path);
129 }
130
131 /* There better be a username attached to the fs. */
132 if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
133 return SVN_FS__ERR_NO_USER(trail->fs);
134
135 /* Is the caller attempting to lock an out-of-date working file? */
136 if (SVN_IS_VALID_REVNUM(args->current_rev))
137 {
138 svn_revnum_t created_rev;
139 SVN_ERR(svn_fs_base__get_path_created_rev(&created_rev, args->path,
140 trail, trail->pool));
141
142 /* SVN_INVALID_REVNUM means the path doesn't exist. So
143 apparently somebody is trying to lock something in their
144 working copy, but somebody else has deleted the thing
145 from HEAD. That counts as being 'out of date'. */
146 if (! SVN_IS_VALID_REVNUM(created_rev))
147 return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
148 "Path '%s' doesn't exist in HEAD revision",
149 args->path);
150
151 if (args->current_rev < created_rev)
152 return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
153 "Lock failed: newer version of '%s' exists",
154 args->path);
155 }
156
157 /* If the caller provided a TOKEN, we *really* need to see
158 if a lock already exists with that token, and if so, verify that
159 the lock's path matches PATH. Otherwise we run the risk of
160 breaking the 1-to-1 mapping of lock tokens to locked paths. */
161 if (args->token)
162 {
163 svn_lock_t *lock_from_token;
164 svn_error_t *err = svn_fs_bdb__lock_get(&lock_from_token, trail->fs,
165 args->token, trail,
166 trail->pool);
167 if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
168 || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
169 {
170 svn_error_clear(err);
171 }
172 else
173 {
174 SVN_ERR(err);
175 if (strcmp(lock_from_token->path, args->path) != 0)
176 return svn_error_create(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
177 "Lock failed: token refers to existing "
178 "lock with non-matching path.");
179 }
180 }
181
182 /* Is the path already locked?
183
184 Note that this next function call will automatically ignore any
185 errors about {the path not existing as a key, the path's token
186 not existing as a key, the lock just having been expired}. And
187 that's totally fine. Any of these three errors are perfectly
188 acceptable to ignore; it means that the path is now free and
189 clear for locking, because the bdb funcs just cleared out both
190 of the tables for us. */
191 SVN_ERR(svn_fs_base__get_lock_helper(&existing_lock, args->path,
192 trail, trail->pool));
193 if (existing_lock)
194 {
195 if (! args->steal_lock)
196 {
197 /* Sorry, the path is already locked. */
198 return SVN_FS__ERR_PATH_ALREADY_LOCKED(trail->fs,
199 existing_lock);
200 }
201 else
202 {
203 /* ARGS->steal_lock is set, so fs_username is "stealing" the
204 lock from lock->owner. Destroy the existing lock. */
205 SVN_ERR(delete_lock_and_token(existing_lock->token,
206 existing_lock->path, trail));
207 }
208 }
209
210 /* Create a new lock, and add it to the tables. */
211 lock = svn_lock_create(args->result_pool);
212 if (args->token)
213 lock->token = apr_pstrdup(args->result_pool, args->token);
214 else
215 SVN_ERR(svn_fs_base__generate_lock_token(&(lock->token), trail->fs,
216 args->result_pool));
217 lock->path = args->path; /* Already in result_pool. */
218 lock->owner = apr_pstrdup(args->result_pool, trail->fs->access_ctx->username);
219 lock->comment = apr_pstrdup(args->result_pool, args->comment);
220 lock->is_dav_comment = args->is_dav_comment;
221 lock->creation_date = apr_time_now();
222 lock->expiration_date = args->expiration_date;
223 SVN_ERR(add_lock_and_token(lock, lock->token, args->path, trail));
224 *(args->lock_p) = lock;
225
226 return SVN_NO_ERROR;
227 }
228
229
230
231 svn_error_t *
svn_fs_base__lock(svn_fs_t * fs,apr_hash_t * targets,const char * comment,svn_boolean_t is_dav_comment,apr_time_t expiration_date,svn_boolean_t steal_lock,svn_fs_lock_callback_t lock_callback,void * lock_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)232 svn_fs_base__lock(svn_fs_t *fs,
233 apr_hash_t *targets,
234 const char *comment,
235 svn_boolean_t is_dav_comment,
236 apr_time_t expiration_date,
237 svn_boolean_t steal_lock,
238 svn_fs_lock_callback_t lock_callback,
239 void *lock_baton,
240 apr_pool_t *result_pool,
241 apr_pool_t *scratch_pool)
242 {
243 apr_hash_index_t *hi;
244 svn_error_t *cb_err = SVN_NO_ERROR;
245 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
246 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
247
248 SVN_ERR(svn_fs__check_fs(fs, TRUE));
249 SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, scratch_pool));
250
251 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
252 {
253 struct lock_args args;
254 const char *path = apr_hash_this_key(hi);
255 const svn_fs_lock_target_t *target = apr_hash_this_val(hi);
256 svn_lock_t *lock;
257 svn_error_t *err = NULL;
258
259 svn_pool_clear(iterpool);
260 args.lock_p = &lock;
261 args.path = svn_fs__canonicalize_abspath(path, result_pool);
262 args.token = target->token;
263 args.comment = comment;
264 args.is_dav_comment = is_dav_comment;
265 args.steal_lock = steal_lock;
266 args.expiration_date = expiration_date;
267 args.current_rev = target->current_rev;
268 args.result_pool = result_pool;
269
270 if (SVN_IS_VALID_REVNUM(target->current_rev))
271 {
272 if (target->current_rev > youngest_rev)
273 err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
274 _("No such revision %ld"),
275 target->current_rev);
276 }
277
278 if (!err)
279 err = svn_fs_base__retry_txn(fs, txn_body_lock, &args, TRUE,
280 iterpool);
281 if (!cb_err && lock_callback)
282 cb_err = lock_callback(lock_baton, args.path, lock, err, iterpool);
283 svn_error_clear(err);
284 }
285 svn_pool_destroy(iterpool);
286
287 return svn_error_trace(cb_err);
288 }
289
290
291 svn_error_t *
svn_fs_base__generate_lock_token(const char ** token,svn_fs_t * fs,apr_pool_t * pool)292 svn_fs_base__generate_lock_token(const char **token,
293 svn_fs_t *fs,
294 apr_pool_t *pool)
295 {
296 /* Notice that 'fs' is currently unused. But perhaps someday, we'll
297 want to use the fs UUID + some incremented number? For now, we
298 generate a URI that matches the DAV RFC. We could change this to
299 some other URI scheme someday, if we wish. */
300 *token = apr_pstrcat(pool, "opaquelocktoken:",
301 svn_uuid_generate(pool), SVN_VA_NULL);
302 return SVN_NO_ERROR;
303 }
304
305
306 /* The effective arguments for txn_body_unlock() below. */
307 struct unlock_args
308 {
309 const char *path;
310 const char *token;
311 svn_boolean_t break_lock;
312 };
313
314
315 /* The body of svn_fs_base__unlock(), which see.
316
317 BATON is a 'struct unlock_args *' holding the effective arguments.
318 BATON->path is the canonical path and BATON->token is the token.
319 For the other arguments, see svn_fs_unlock_many().
320
321 This implements the svn_fs_base__retry_txn() 'body' callback type.
322 */
323 static svn_error_t *
txn_body_unlock(void * baton,trail_t * trail)324 txn_body_unlock(void *baton, trail_t *trail)
325 {
326 struct unlock_args *args = baton;
327 const char *lock_token;
328 svn_lock_t *lock;
329
330 /* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */
331 SVN_ERR(svn_fs_bdb__lock_token_get(&lock_token, trail->fs, args->path,
332 trail, trail->pool));
333
334 /* If not breaking the lock, we need to do some more checking. */
335 if (!args->break_lock)
336 {
337 /* Sanity check: The lock token must exist, and must match. */
338 if (args->token == NULL)
339 return svn_fs_base__err_no_lock_token(trail->fs, args->path);
340 else if (strcmp(lock_token, args->token) != 0)
341 return SVN_FS__ERR_NO_SUCH_LOCK(trail->fs, args->path);
342
343 SVN_ERR(svn_fs_bdb__lock_get(&lock, trail->fs, lock_token,
344 trail, trail->pool));
345
346 /* There better be a username attached to the fs. */
347 if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
348 return SVN_FS__ERR_NO_USER(trail->fs);
349
350 /* And that username better be the same as the lock's owner. */
351 if (strcmp(trail->fs->access_ctx->username, lock->owner) != 0)
352 return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
353 trail->fs,
354 trail->fs->access_ctx->username,
355 lock->owner);
356 }
357
358 /* Remove a row from each of the locking tables. */
359 return delete_lock_and_token(lock_token, args->path, trail);
360 }
361
362
363 svn_error_t *
svn_fs_base__unlock(svn_fs_t * fs,apr_hash_t * targets,svn_boolean_t break_lock,svn_fs_lock_callback_t lock_callback,void * lock_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)364 svn_fs_base__unlock(svn_fs_t *fs,
365 apr_hash_t *targets,
366 svn_boolean_t break_lock,
367 svn_fs_lock_callback_t lock_callback,
368 void *lock_baton,
369 apr_pool_t *result_pool,
370 apr_pool_t *scratch_pool)
371 {
372 apr_hash_index_t *hi;
373 svn_error_t *cb_err = SVN_NO_ERROR;
374 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
375
376 SVN_ERR(svn_fs__check_fs(fs, TRUE));
377
378 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
379 {
380 struct unlock_args args;
381 const char *path = apr_hash_this_key(hi);
382 const char *token = apr_hash_this_val(hi);
383 svn_error_t *err;
384
385 svn_pool_clear(iterpool);
386 args.path = svn_fs__canonicalize_abspath(path, result_pool);
387 args.token = token;
388 args.break_lock = break_lock;
389
390 err = svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE,
391 iterpool);
392 if (!cb_err && lock_callback)
393 cb_err = lock_callback(lock_baton, path, NULL, err, iterpool);
394 svn_error_clear(err);
395 }
396 svn_pool_destroy(iterpool);
397
398 return svn_error_trace(cb_err);
399 }
400
401
402 svn_error_t *
svn_fs_base__get_lock_helper(svn_lock_t ** lock_p,const char * path,trail_t * trail,apr_pool_t * pool)403 svn_fs_base__get_lock_helper(svn_lock_t **lock_p,
404 const char *path,
405 trail_t *trail,
406 apr_pool_t *pool)
407 {
408 const char *lock_token;
409 svn_error_t *err;
410
411 err = svn_fs_bdb__lock_token_get(&lock_token, trail->fs, path,
412 trail, pool);
413
414 /* We've deliberately decided that this function doesn't tell the
415 caller *why* the lock is unavailable. */
416 if (err && ((err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK)
417 || (err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
418 || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
419 {
420 svn_error_clear(err);
421 *lock_p = NULL;
422 return SVN_NO_ERROR;
423 }
424 else
425 SVN_ERR(err);
426
427 /* Same situation here. */
428 err = svn_fs_bdb__lock_get(lock_p, trail->fs, lock_token, trail, pool);
429 if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
430 || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
431 {
432 svn_error_clear(err);
433 *lock_p = NULL;
434 return SVN_NO_ERROR;
435 }
436 else
437 SVN_ERR(err);
438
439 return svn_error_trace(err);
440 }
441
442
443 struct lock_token_get_args
444 {
445 svn_lock_t **lock_p;
446 const char *path;
447 };
448
449
450 static svn_error_t *
txn_body_get_lock(void * baton,trail_t * trail)451 txn_body_get_lock(void *baton, trail_t *trail)
452 {
453 struct lock_token_get_args *args = baton;
454 return svn_fs_base__get_lock_helper(args->lock_p, args->path,
455 trail, trail->pool);
456 }
457
458
459 svn_error_t *
svn_fs_base__get_lock(svn_lock_t ** lock,svn_fs_t * fs,const char * path,apr_pool_t * pool)460 svn_fs_base__get_lock(svn_lock_t **lock,
461 svn_fs_t *fs,
462 const char *path,
463 apr_pool_t *pool)
464 {
465 struct lock_token_get_args args;
466
467 SVN_ERR(svn_fs__check_fs(fs, TRUE));
468
469 args.path = svn_fs__canonicalize_abspath(path, pool);
470 args.lock_p = lock;
471 return svn_fs_base__retry_txn(fs, txn_body_get_lock, &args, FALSE, pool);
472 }
473
474 /* Implements `svn_fs_get_locks_callback_t', spooling lock information
475 to a stream as the filesystem provides it. BATON is an 'svn_stream_t *'
476 object pointing to the stream. We'll write the spool stream with a
477 format like so:
478
479 SKEL1_LEN "\n" SKEL1 "\n" SKEL2_LEN "\n" SKEL2 "\n" ...
480
481 where each skel is a lock skel (the same format we use to store
482 locks in the `locks' table). */
483 static svn_error_t *
spool_locks_info(void * baton,svn_lock_t * lock,apr_pool_t * pool)484 spool_locks_info(void *baton,
485 svn_lock_t *lock,
486 apr_pool_t *pool)
487 {
488 svn_skel_t *lock_skel;
489 svn_stream_t *stream = baton;
490 const char *skel_len;
491 svn_stringbuf_t *skel_buf;
492 apr_size_t len;
493
494 SVN_ERR(svn_fs_base__unparse_lock_skel(&lock_skel, lock, pool));
495 skel_buf = svn_skel__unparse(lock_skel, pool);
496 skel_len = apr_psprintf(pool, "%" APR_SIZE_T_FMT "\n", skel_buf->len);
497 len = strlen(skel_len);
498 SVN_ERR(svn_stream_write(stream, skel_len, &len));
499 len = skel_buf->len;
500 SVN_ERR(svn_stream_write(stream, skel_buf->data, &len));
501 len = 1;
502 return svn_stream_write(stream, "\n", &len);
503 }
504
505
506 struct locks_get_args
507 {
508 const char *path;
509 svn_depth_t depth;
510 svn_stream_t *stream;
511 };
512
513
514 static svn_error_t *
txn_body_get_locks(void * baton,trail_t * trail)515 txn_body_get_locks(void *baton, trail_t *trail)
516 {
517 struct locks_get_args *args = baton;
518 return svn_fs_bdb__locks_get(trail->fs, args->path, args->depth,
519 spool_locks_info, args->stream,
520 trail, trail->pool);
521 }
522
523
524 svn_error_t *
svn_fs_base__get_locks(svn_fs_t * fs,const char * path,svn_depth_t depth,svn_fs_get_locks_callback_t get_locks_func,void * get_locks_baton,apr_pool_t * pool)525 svn_fs_base__get_locks(svn_fs_t *fs,
526 const char *path,
527 svn_depth_t depth,
528 svn_fs_get_locks_callback_t get_locks_func,
529 void *get_locks_baton,
530 apr_pool_t *pool)
531 {
532 struct locks_get_args args;
533 svn_stream_t *stream;
534 svn_stringbuf_t *buf;
535 svn_boolean_t eof;
536 apr_pool_t *iterpool = svn_pool_create(pool);
537
538 SVN_ERR(svn_fs__check_fs(fs, TRUE));
539
540 args.path = svn_fs__canonicalize_abspath(path, pool);
541 args.depth = depth;
542 /* Enough for 100+ locks if the comments are small. */
543 args.stream = svn_stream__from_spillbuf(svn_spillbuf__create(4 * 1024 /* blocksize */,
544 64 * 1024 /* maxsize */,
545 pool),
546 pool);
547 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool));
548
549 /* Read the stream calling GET_LOCKS_FUNC(). */
550 stream = args.stream;
551
552 while (1)
553 {
554 apr_size_t len, skel_len;
555 char c, *skel_buf;
556 svn_skel_t *lock_skel;
557 svn_lock_t *lock;
558 apr_uint64_t ui64;
559 svn_error_t *err;
560
561 svn_pool_clear(iterpool);
562
563 /* Read a skel length line and parse it for the skel's length. */
564 SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool));
565 if (eof)
566 break;
567 err = svn_cstring_strtoui64(&ui64, buf->data, 0, APR_SIZE_MAX, 10);
568 if (err)
569 return svn_error_create(SVN_ERR_MALFORMED_FILE, err, NULL);
570 skel_len = (apr_size_t)ui64;
571
572 /* Now read that much into a buffer. */
573 skel_buf = apr_palloc(pool, skel_len + 1);
574 SVN_ERR(svn_stream_read_full(stream, skel_buf, &skel_len));
575 skel_buf[skel_len] = '\0';
576
577 /* Read the extra newline that follows the skel. */
578 len = 1;
579 SVN_ERR(svn_stream_read_full(stream, &c, &len));
580 if (c != '\n')
581 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL);
582
583 /* Parse the skel into a lock, and notify the caller. */
584 lock_skel = svn_skel__parse(skel_buf, skel_len, iterpool);
585 SVN_ERR(svn_fs_base__parse_lock_skel(&lock, lock_skel, iterpool));
586 SVN_ERR(get_locks_func(get_locks_baton, lock, iterpool));
587 }
588
589 SVN_ERR(svn_stream_close(stream));
590 svn_pool_destroy(iterpool);
591 return SVN_NO_ERROR;
592 }
593
594
595
596 /* Utility function: verify that a lock can be used.
597
598 If no username is attached to the FS, return SVN_ERR_FS_NO_USER.
599
600 If the FS username doesn't match LOCK's owner, return
601 SVN_ERR_FS_LOCK_OWNER_MISMATCH.
602
603 If FS hasn't been supplied with a matching lock-token for LOCK,
604 return SVN_ERR_FS_BAD_LOCK_TOKEN.
605
606 Otherwise return SVN_NO_ERROR.
607 */
608 static svn_error_t *
verify_lock(svn_fs_t * fs,svn_lock_t * lock,apr_pool_t * pool)609 verify_lock(svn_fs_t *fs,
610 svn_lock_t *lock,
611 apr_pool_t *pool)
612 {
613 if ((! fs->access_ctx) || (! fs->access_ctx->username))
614 return svn_error_createf
615 (SVN_ERR_FS_NO_USER, NULL,
616 _("Cannot verify lock on path '%s'; no username available"),
617 lock->path);
618
619 else if (strcmp(fs->access_ctx->username, lock->owner) != 0)
620 return svn_error_createf
621 (SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL,
622 _("User '%s' does not own lock on path '%s' (currently locked by '%s')"),
623 fs->access_ctx->username, lock->path, lock->owner);
624
625 else if (svn_hash_gets(fs->access_ctx->lock_tokens, lock->token) == NULL)
626 return svn_error_createf
627 (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
628 _("Cannot verify lock on path '%s'; no matching lock-token available"),
629 lock->path);
630
631 return SVN_NO_ERROR;
632 }
633
634
635 /* This implements the svn_fs_get_locks_callback_t interface, where
636 BATON is just an svn_fs_t object. */
637 static svn_error_t *
get_locks_callback(void * baton,svn_lock_t * lock,apr_pool_t * pool)638 get_locks_callback(void *baton,
639 svn_lock_t *lock,
640 apr_pool_t *pool)
641 {
642 return verify_lock(baton, lock, pool);
643 }
644
645
646 /* The main routine for lock enforcement, used throughout libsvn_fs_base. */
647 svn_error_t *
svn_fs_base__allow_locked_operation(const char * path,svn_boolean_t recurse,trail_t * trail,apr_pool_t * pool)648 svn_fs_base__allow_locked_operation(const char *path,
649 svn_boolean_t recurse,
650 trail_t *trail,
651 apr_pool_t *pool)
652 {
653 if (recurse)
654 {
655 /* Discover all locks at or below the path. */
656 SVN_ERR(svn_fs_bdb__locks_get(trail->fs, path, svn_depth_infinity,
657 get_locks_callback,
658 trail->fs, trail, pool));
659 }
660 else
661 {
662 svn_lock_t *lock;
663
664 /* Discover any lock attached to the path. */
665 SVN_ERR(svn_fs_base__get_lock_helper(&lock, path, trail, pool));
666 if (lock)
667 SVN_ERR(verify_lock(trail->fs, lock, pool));
668 }
669 return SVN_NO_ERROR;
670 }
671