1 /* lock-test.c --- tests for the filesystem locking functions
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 <string.h>
24 #include <apr_pools.h>
25 #include <apr_time.h>
26
27 #include "../svn_test.h"
28
29 #include "svn_error.h"
30 #include "svn_fs.h"
31 #include "svn_hash.h"
32
33 #include "../svn_test_fs.h"
34
35
36 /*-----------------------------------------------------------------*/
37
38 /** Helper functions **/
39
40 /* Implementations of the svn_fs_get_locks_callback_t interface and
41 baton, for verifying expected output from svn_fs_get_locks(). */
42
43 struct get_locks_baton_t
44 {
45 apr_hash_t *locks;
46 };
47
48 static svn_error_t *
get_locks_callback(void * baton,svn_lock_t * lock,apr_pool_t * pool)49 get_locks_callback(void *baton,
50 svn_lock_t *lock,
51 apr_pool_t *pool)
52 {
53 struct get_locks_baton_t *b = baton;
54 apr_pool_t *hash_pool = apr_hash_pool_get(b->locks);
55 svn_string_t *lock_path = svn_string_create(lock->path, hash_pool);
56
57 if (!apr_hash_get(b->locks, lock_path->data, lock_path->len))
58 {
59 apr_hash_set(b->locks, lock_path->data, lock_path->len,
60 svn_lock_dup(lock, hash_pool));
61 return SVN_NO_ERROR;
62 }
63 else
64 {
65 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
66 "Lock for path '%s' is being reported twice.",
67 lock->path);
68 }
69 }
70
71 /* A factory function. */
72
73 static struct get_locks_baton_t *
make_get_locks_baton(apr_pool_t * pool)74 make_get_locks_baton(apr_pool_t *pool)
75 {
76 struct get_locks_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
77 baton->locks = apr_hash_make(pool);
78 return baton;
79 }
80
81
82 /* And verification function(s). */
83
84 static svn_error_t *
verify_matching_lock_paths(struct get_locks_baton_t * baton,const char * expected_paths[],apr_size_t num_expected_paths,apr_pool_t * pool)85 verify_matching_lock_paths(struct get_locks_baton_t *baton,
86 const char *expected_paths[],
87 apr_size_t num_expected_paths,
88 apr_pool_t *pool)
89 {
90 apr_size_t i;
91 if (num_expected_paths != apr_hash_count(baton->locks))
92 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
93 "Unexpected number of locks.");
94 for (i = 0; i < num_expected_paths; i++)
95 {
96 const char *path = expected_paths[i];
97 if (! apr_hash_get(baton->locks, path, APR_HASH_KEY_STRING))
98 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
99 "Missing lock for path '%s'", path);
100 }
101 return SVN_NO_ERROR;
102 }
103
104
105 /* Create a filesystem in a directory called NAME, and populate it with
106 * the standard Greek tree. Set *FS_P to the new filesystem object and
107 * *NEWREV_P to the head revision number. Unwanted outputs may be NULL. */
108 static svn_error_t *
create_greek_fs(svn_fs_t ** fs_p,svn_revnum_t * newrev_p,const char * name,const svn_test_opts_t * opts,apr_pool_t * pool)109 create_greek_fs(svn_fs_t **fs_p,
110 svn_revnum_t *newrev_p,
111 const char *name,
112 const svn_test_opts_t *opts,
113 apr_pool_t *pool)
114 {
115 svn_fs_t *fs;
116 svn_fs_txn_t *txn;
117 svn_fs_root_t *txn_root;
118 const char *conflict;
119 svn_revnum_t newrev;
120
121 /* Prepare a filesystem and a new txn. */
122 SVN_ERR(svn_test__create_fs(&fs, name, opts, pool));
123 SVN_ERR(svn_fs_begin_txn2(&txn, fs, 0, SVN_FS_TXN_CHECK_LOCKS, pool));
124 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
125
126 /* Create the greek tree and commit it. */
127 SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
128 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
129 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(newrev));
130
131 if (fs_p)
132 *fs_p = fs;
133 if (newrev_p)
134 *newrev_p = newrev;
135 return SVN_NO_ERROR;
136 }
137
138
139 /*-----------------------------------------------------------------*/
140
141 /** The actual lock-tests called by `make check` **/
142
143
144
145 /* Test that we can create a lock--nothing more. */
146 static svn_error_t *
lock_only(const svn_test_opts_t * opts,apr_pool_t * pool)147 lock_only(const svn_test_opts_t *opts,
148 apr_pool_t *pool)
149 {
150 svn_fs_t *fs;
151 svn_fs_access_t *access;
152 svn_lock_t *mylock;
153
154 SVN_ERR(create_greek_fs(&fs, NULL, "test-repo-lock-only",
155 opts, pool));
156
157 /* We are now 'bubba'. */
158 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
159 SVN_ERR(svn_fs_set_access(fs, access));
160
161 /* Lock /A/D/G/rho. */
162 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
163 SVN_INVALID_REVNUM, FALSE, pool));
164
165 return SVN_NO_ERROR;
166 }
167
168
169
170
171
172 /* Test that we can create, fetch, and destroy a lock. It exercises
173 each of the five public fs locking functions. */
174 static svn_error_t *
lookup_lock_by_path(const svn_test_opts_t * opts,apr_pool_t * pool)175 lookup_lock_by_path(const svn_test_opts_t *opts,
176 apr_pool_t *pool)
177 {
178 svn_fs_t *fs;
179 svn_fs_access_t *access;
180 svn_lock_t *mylock, *somelock;
181
182 SVN_ERR(create_greek_fs(&fs, NULL, "test-repo-lookup-lock-by-path",
183 opts, pool));
184
185 /* We are now 'bubba'. */
186 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
187 SVN_ERR(svn_fs_set_access(fs, access));
188
189 /* Lock /A/D/G/rho. */
190 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
191 SVN_INVALID_REVNUM, FALSE, pool));
192
193 /* Can we look up the lock by path? */
194 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
195 if ((! somelock) || (strcmp(somelock->token, mylock->token) != 0))
196 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
197 "Couldn't look up a lock by pathname.");
198
199 return SVN_NO_ERROR;
200 }
201
202 /* Test that we can create a lock outside of the fs and attach it to a
203 path. */
204 static svn_error_t *
attach_lock(const svn_test_opts_t * opts,apr_pool_t * pool)205 attach_lock(const svn_test_opts_t *opts,
206 apr_pool_t *pool)
207 {
208 svn_fs_t *fs;
209 svn_fs_access_t *access;
210 svn_lock_t *somelock;
211 svn_lock_t *mylock;
212 const char *token;
213
214 SVN_ERR(create_greek_fs(&fs, NULL, "test-repo-attach-lock",
215 opts, pool));
216
217 /* We are now 'bubba'. */
218 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
219 SVN_ERR(svn_fs_set_access(fs, access));
220
221 SVN_ERR(svn_fs_generate_lock_token(&token, fs, pool));
222 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", token,
223 "This is a comment. Yay comment!", 0,
224 apr_time_now() + apr_time_from_sec(3),
225 SVN_INVALID_REVNUM, FALSE, pool));
226
227 /* Can we look up the lock by path? */
228 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
229 if ((! somelock) || (strcmp(somelock->token, mylock->token) != 0))
230 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
231 "Couldn't look up a lock by pathname.");
232
233 /* Unlock /A/D/G/rho, and verify that it's gone. */
234 SVN_ERR(svn_fs_unlock(fs, mylock->path, mylock->token, 0, pool));
235 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
236 if (somelock)
237 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
238 "Removed a lock, but it's still there.");
239
240 return SVN_NO_ERROR;
241 }
242
243
244 /* Test that we can get all locks under a directory. */
245 static svn_error_t *
get_locks(const svn_test_opts_t * opts,apr_pool_t * pool)246 get_locks(const svn_test_opts_t *opts,
247 apr_pool_t *pool)
248 {
249 svn_fs_t *fs;
250 svn_fs_access_t *access;
251 svn_lock_t *mylock;
252 struct get_locks_baton_t *get_locks_baton;
253 apr_size_t i, num_expected_paths;
254
255 SVN_ERR(create_greek_fs(&fs, NULL, "test-repo-get-locks",
256 opts, pool));
257
258 /* We are now 'bubba'. */
259 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
260 SVN_ERR(svn_fs_set_access(fs, access));
261
262 /* Lock our paths; verify from "/". */
263 {
264 static const char *expected_paths[] = {
265 "/A/D/G/pi",
266 "/A/D/G/rho",
267 "/A/D/G/tau",
268 "/A/D/H/psi",
269 "/A/D/H/chi",
270 "/A/D/H/omega",
271 "/A/B/E/alpha",
272 "/A/B/E/beta",
273 };
274 num_expected_paths = sizeof(expected_paths) / sizeof(const char *);
275 for (i = 0; i < num_expected_paths; i++)
276 {
277 SVN_ERR(svn_fs_lock(&mylock, fs, expected_paths[i], NULL, "", 0, 0,
278 SVN_INVALID_REVNUM, FALSE, pool));
279 }
280 get_locks_baton = make_get_locks_baton(pool);
281 SVN_ERR(svn_fs_get_locks(fs, "", get_locks_callback,
282 get_locks_baton, pool));
283 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
284 num_expected_paths, pool));
285 }
286
287 /* Verify from "/A/B". */
288 {
289 static const char *expected_paths[] = {
290 "/A/B/E/alpha",
291 "/A/B/E/beta",
292 };
293 num_expected_paths = sizeof(expected_paths) / sizeof(const char *);
294 get_locks_baton = make_get_locks_baton(pool);
295 SVN_ERR(svn_fs_get_locks(fs, "A/B", get_locks_callback,
296 get_locks_baton, pool));
297 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
298 num_expected_paths, pool));
299 }
300
301 /* Verify from "/A/D". */
302 {
303 static const char *expected_paths[] = {
304 "/A/D/G/pi",
305 "/A/D/G/rho",
306 "/A/D/G/tau",
307 "/A/D/H/psi",
308 "/A/D/H/chi",
309 "/A/D/H/omega",
310 };
311 num_expected_paths = sizeof(expected_paths) / sizeof(const char *);
312 get_locks_baton = make_get_locks_baton(pool);
313 SVN_ERR(svn_fs_get_locks(fs, "A/D", get_locks_callback,
314 get_locks_baton, pool));
315 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
316 num_expected_paths, pool));
317 }
318
319 /* Verify from "/A/D/G". */
320 {
321 static const char *expected_paths[] = {
322 "/A/D/G/pi",
323 "/A/D/G/rho",
324 "/A/D/G/tau",
325 };
326 num_expected_paths = sizeof(expected_paths) / sizeof(const char *);
327 get_locks_baton = make_get_locks_baton(pool);
328 SVN_ERR(svn_fs_get_locks(fs, "A/D/G", get_locks_callback,
329 get_locks_baton, pool));
330 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
331 num_expected_paths, pool));
332 }
333
334 /* Verify from "/A/D/H/omega". */
335 {
336 static const char *expected_paths[] = {
337 "/A/D/H/omega",
338 };
339 num_expected_paths = sizeof(expected_paths) / sizeof(const char *);
340 get_locks_baton = make_get_locks_baton(pool);
341 SVN_ERR(svn_fs_get_locks(fs, "A/D/H/omega", get_locks_callback,
342 get_locks_baton, pool));
343 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
344 num_expected_paths, pool));
345 }
346
347 /* Verify from "/iota" (which wasn't locked... tricky...). */
348 {
349 static const char *expected_paths[] = { 0 };
350 num_expected_paths = 0;
351 get_locks_baton = make_get_locks_baton(pool);
352 SVN_ERR(svn_fs_get_locks(fs, "iota", get_locks_callback,
353 get_locks_baton, pool));
354 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
355 num_expected_paths, pool));
356 }
357
358 /* A path that is longer and alphabetically earlier than some locked
359 paths, this exercises the r1205848 BDB lock code. */
360 {
361 static const char *expected_paths[] = { 0 };
362 num_expected_paths = 0;
363 get_locks_baton = make_get_locks_baton(pool);
364 SVN_ERR(svn_fs_get_locks(fs, "A/D/H/ABCDEFGHIJKLMNOPQR", get_locks_callback,
365 get_locks_baton, pool));
366 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
367 num_expected_paths, pool));
368 }
369
370 return SVN_NO_ERROR;
371 }
372
373
374 /* Test that we can create, fetch, and destroy a lock. It exercises
375 each of the five public fs locking functions. */
376 static svn_error_t *
basic_lock(const svn_test_opts_t * opts,apr_pool_t * pool)377 basic_lock(const svn_test_opts_t *opts,
378 apr_pool_t *pool)
379 {
380 svn_fs_t *fs;
381 svn_fs_access_t *access;
382 svn_lock_t *mylock, *somelock;
383
384 SVN_ERR(create_greek_fs(&fs, NULL, "test-repo-basic-lock",
385 opts, pool));
386
387 /* We are now 'bubba'. */
388 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
389 SVN_ERR(svn_fs_set_access(fs, access));
390
391 /* Lock /A/D/G/rho. */
392 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
393 SVN_INVALID_REVNUM, FALSE, pool));
394
395 /* Can we look up the lock by path? */
396 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
397 if ((! somelock) || (strcmp(somelock->token, mylock->token) != 0))
398 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
399 "Couldn't look up a lock by pathname.");
400
401 /* Unlock /A/D/G/rho, and verify that it's gone. */
402 SVN_ERR(svn_fs_unlock(fs, mylock->path, mylock->token, 0, pool));
403 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
404 if (somelock)
405 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
406 "Removed a lock, but it's still there.");
407
408 return SVN_NO_ERROR;
409 }
410
411
412
413 /* Test that locks are enforced -- specifically that both a username
414 and token are required to make use of the lock. */
415 static svn_error_t *
lock_credentials(const svn_test_opts_t * opts,apr_pool_t * pool)416 lock_credentials(const svn_test_opts_t *opts,
417 apr_pool_t *pool)
418 {
419 svn_fs_t *fs;
420 svn_fs_txn_t *txn;
421 svn_fs_root_t *txn_root;
422 const char *conflict;
423 svn_revnum_t newrev;
424 svn_fs_access_t *access;
425 svn_lock_t *mylock;
426 svn_error_t *err;
427
428 SVN_ERR(create_greek_fs(&fs, &newrev, "test-repo-lock-credentials",
429 opts, pool));
430
431 /* We are now 'bubba'. */
432 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
433 SVN_ERR(svn_fs_set_access(fs, access));
434
435 /* Lock /A/D/G/rho. */
436 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
437 SVN_INVALID_REVNUM, FALSE, pool));
438
439 /* Push the proper lock-token into the fs access context. */
440 SVN_ERR(svn_fs_access_add_lock_token(access, mylock->token));
441
442 /* Make a new transaction and change rho. */
443 SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
444 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
445 SVN_ERR(svn_test__set_file_contents(txn_root, "/A/D/G/rho",
446 "new contents", pool));
447
448 /* We are no longer 'bubba'. We're nobody. */
449 SVN_ERR(svn_fs_set_access(fs, NULL));
450
451 /* Try to commit the file change. Should fail, because we're nobody. */
452 err = svn_fs_commit_txn(&conflict, &newrev, txn, pool);
453 SVN_TEST_ASSERT(! SVN_IS_VALID_REVNUM(newrev));
454 if (! err)
455 return svn_error_create
456 (SVN_ERR_TEST_FAILED, NULL,
457 "Uhoh, able to commit locked file without any fs username.");
458 svn_error_clear(err);
459
460 /* We are now 'hortense'. */
461 SVN_ERR(svn_fs_create_access(&access, "hortense", pool));
462 SVN_ERR(svn_fs_set_access(fs, access));
463
464 /* Try to commit the file change. Should fail, because we're 'hortense'. */
465 err = svn_fs_commit_txn(&conflict, &newrev, txn, pool);
466 SVN_TEST_ASSERT(! SVN_IS_VALID_REVNUM(newrev));
467 if (! err)
468 return svn_error_create
469 (SVN_ERR_TEST_FAILED, NULL,
470 "Uhoh, able to commit locked file as non-owner.");
471 svn_error_clear(err);
472
473 /* Be 'bubba' again. */
474 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
475 SVN_ERR(svn_fs_set_access(fs, access));
476
477 /* Try to commit the file change. Should fail, because there's no token. */
478 err = svn_fs_commit_txn(&conflict, &newrev, txn, pool);
479 SVN_TEST_ASSERT(! SVN_IS_VALID_REVNUM(newrev));
480 if (! err)
481 return svn_error_create
482 (SVN_ERR_TEST_FAILED, NULL,
483 "Uhoh, able to commit locked file with no lock token.");
484 svn_error_clear(err);
485
486 /* Push the proper lock-token into the fs access context. */
487 SVN_ERR(svn_fs_access_add_lock_token(access, mylock->token));
488
489 /* Commit should now succeed. */
490 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
491 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(newrev));
492
493 return SVN_NO_ERROR;
494 }
495
496
497
498 /* Test that locks are enforced at commit time. Somebody might lock
499 something behind your back, right before you run
500 svn_fs_commit_txn(). Also, this test verifies that recursive
501 lock-checks on directories is working properly. */
502 static svn_error_t *
final_lock_check(const svn_test_opts_t * opts,apr_pool_t * pool)503 final_lock_check(const svn_test_opts_t *opts,
504 apr_pool_t *pool)
505 {
506 svn_fs_t *fs;
507 svn_fs_txn_t *txn;
508 svn_fs_root_t *txn_root;
509 const char *conflict;
510 svn_revnum_t newrev;
511 svn_fs_access_t *access;
512 svn_lock_t *mylock;
513 svn_error_t *err;
514
515 SVN_ERR(create_greek_fs(&fs, &newrev, "test-repo-final-lock-check",
516 opts, pool));
517
518 /* Make a new transaction and delete "/A" */
519 SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
520 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
521 SVN_ERR(svn_fs_delete(txn_root, "/A", pool));
522
523 /* Become 'bubba' and lock "/A/D/G/rho". */
524 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
525 SVN_ERR(svn_fs_set_access(fs, access));
526 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
527 SVN_INVALID_REVNUM, FALSE, pool));
528
529 /* We are no longer 'bubba'. We're nobody. */
530 SVN_ERR(svn_fs_set_access(fs, NULL));
531
532 /* Try to commit the transaction. Should fail, because a child of
533 the deleted directory is locked by someone else. */
534 err = svn_fs_commit_txn(&conflict, &newrev, txn, pool);
535 SVN_TEST_ASSERT(! SVN_IS_VALID_REVNUM(newrev));
536 if (! err)
537 return svn_error_create
538 (SVN_ERR_TEST_FAILED, NULL,
539 "Uhoh, able to commit dir deletion when a child is locked.");
540 svn_error_clear(err);
541
542 /* Supply correct username and token; commit should work. */
543 SVN_ERR(svn_fs_set_access(fs, access));
544 SVN_ERR(svn_fs_access_add_lock_token(access, mylock->token));
545 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
546 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(newrev));
547
548 return SVN_NO_ERROR;
549 }
550
551
552
553 /* If a directory's child is locked by someone else, we should still
554 be able to commit a propchange on the directory. */
555 static svn_error_t *
lock_dir_propchange(const svn_test_opts_t * opts,apr_pool_t * pool)556 lock_dir_propchange(const svn_test_opts_t *opts,
557 apr_pool_t *pool)
558 {
559 svn_fs_t *fs;
560 svn_fs_txn_t *txn;
561 svn_fs_root_t *txn_root;
562 const char *conflict;
563 svn_revnum_t newrev;
564 svn_fs_access_t *access;
565 svn_lock_t *mylock;
566
567 SVN_ERR(create_greek_fs(&fs, &newrev, "test-repo-lock-dir-propchange",
568 opts, pool));
569
570 /* Become 'bubba' and lock "/A/D/G/rho". */
571 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
572 SVN_ERR(svn_fs_set_access(fs, access));
573 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
574 SVN_INVALID_REVNUM, FALSE, pool));
575
576 /* We are no longer 'bubba'. We're nobody. */
577 SVN_ERR(svn_fs_set_access(fs, NULL));
578
579 /* Make a new transaction and make a propchange on "/A" */
580 SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
581 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
582 SVN_ERR(svn_fs_change_node_prop(txn_root, "/A",
583 "foo", svn_string_create("bar", pool),
584 pool));
585
586 /* Commit should succeed; this means we're doing a non-recursive
587 lock-check on directory, rather than a recursive one. */
588 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
589 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(newrev));
590
591 return SVN_NO_ERROR;
592 }
593
594 /* Test that locks auto-expire correctly. */
595 static svn_error_t *
lock_expiration(const svn_test_opts_t * opts,apr_pool_t * pool)596 lock_expiration(const svn_test_opts_t *opts,
597 apr_pool_t *pool)
598 {
599 svn_fs_t *fs;
600 svn_fs_txn_t *txn;
601 svn_fs_root_t *txn_root;
602 const char *conflict;
603 svn_revnum_t newrev;
604 svn_fs_access_t *access;
605 svn_lock_t *mylock;
606 svn_error_t *err;
607 struct get_locks_baton_t *get_locks_baton;
608
609 SVN_ERR(create_greek_fs(&fs, &newrev, "test-repo-lock-expiration",
610 opts, pool));
611
612 /* Make a new transaction and change rho. */
613 SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
614 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
615 SVN_ERR(svn_test__set_file_contents(txn_root, "/A/D/G/rho",
616 "new contents", pool));
617
618 /* We are now 'bubba'. */
619 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
620 SVN_ERR(svn_fs_set_access(fs, access));
621
622 /* Lock /A/D/G/rho, with an expiration 2 seconds from now. */
623 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0,
624 apr_time_now() + apr_time_from_sec(2),
625 SVN_INVALID_REVNUM, FALSE, pool));
626
627 /* Become nobody. */
628 SVN_ERR(svn_fs_set_access(fs, NULL));
629
630 /* Try to commit. Should fail because we're 'nobody', and the lock
631 hasn't expired yet. */
632 err = svn_fs_commit_txn(&conflict, &newrev, txn, pool);
633 SVN_TEST_ASSERT(! SVN_IS_VALID_REVNUM(newrev));
634 if (! err)
635 return svn_error_create
636 (SVN_ERR_TEST_FAILED, NULL,
637 "Uhoh, able to commit a file that has a non-expired lock.");
638 svn_error_clear(err);
639
640 /* Check that the lock is there, by getting it via the paths parent. */
641 {
642 static const char *expected_paths [] = {
643 "/A/D/G/rho"
644 };
645 apr_size_t num_expected_paths = (sizeof(expected_paths)
646 / sizeof(expected_paths[0]));
647 get_locks_baton = make_get_locks_baton(pool);
648 SVN_ERR(svn_fs_get_locks(fs, "/A/D/G", get_locks_callback,
649 get_locks_baton, pool));
650 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
651 num_expected_paths, pool));
652 }
653
654 /* Sleep 2 seconds, so the lock auto-expires. Anonymous commit
655 should then succeed. */
656 apr_sleep(apr_time_from_sec(3));
657
658 /* Verify that the lock auto-expired even in the recursive case. */
659 {
660 static const char *expected_paths [] = { 0 };
661 apr_size_t num_expected_paths = 0;
662 get_locks_baton = make_get_locks_baton(pool);
663 SVN_ERR(svn_fs_get_locks(fs, "/A/D/G", get_locks_callback,
664 get_locks_baton, pool));
665 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
666 num_expected_paths, pool));
667 }
668
669 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
670 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(newrev));
671
672 return SVN_NO_ERROR;
673 }
674
675 /* Test that a lock can be broken, stolen, or refreshed */
676 static svn_error_t *
lock_break_steal_refresh(const svn_test_opts_t * opts,apr_pool_t * pool)677 lock_break_steal_refresh(const svn_test_opts_t *opts,
678 apr_pool_t *pool)
679 {
680 svn_fs_t *fs;
681 svn_fs_access_t *access;
682 svn_lock_t *mylock, *somelock;
683
684 SVN_ERR(create_greek_fs(&fs, NULL, "test-repo-steal-refresh",
685 opts, pool));
686
687 /* Become 'bubba' and lock "/A/D/G/rho". */
688 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
689 SVN_ERR(svn_fs_set_access(fs, access));
690 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
691 SVN_INVALID_REVNUM, FALSE, pool));
692
693 /* Become 'hortense' and break bubba's lock, then verify it's gone. */
694 SVN_ERR(svn_fs_create_access(&access, "hortense", pool));
695 SVN_ERR(svn_fs_set_access(fs, access));
696 SVN_ERR(svn_fs_unlock(fs, mylock->path, mylock->token,
697 1 /* FORCE BREAK */, pool));
698 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
699 if (somelock)
700 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
701 "Tried to break a lock, but it's still there.");
702
703 /* As hortense, create a new lock, and verify that we own it. */
704 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0,
705 SVN_INVALID_REVNUM, FALSE, pool));
706 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
707 if (strcmp(somelock->owner, mylock->owner) != 0)
708 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
709 "Made a lock, but we don't seem to own it.");
710
711 /* As bubba, steal hortense's lock, creating a new one that expires. */
712 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
713 SVN_ERR(svn_fs_set_access(fs, access));
714 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0,
715 apr_time_now() + apr_time_from_sec(300), /* 5 min. */
716 SVN_INVALID_REVNUM,
717 TRUE /* FORCE STEAL */,
718 pool));
719 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
720 if (strcmp(somelock->owner, mylock->owner) != 0)
721 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
722 "Made a lock, but we don't seem to own it.");
723 if (! somelock->expiration_date)
724 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
725 "Made expiring lock, but seems not to expire.");
726
727 /* Refresh the lock, so that it never expires. */
728 SVN_ERR(svn_fs_lock(&somelock, fs, somelock->path, somelock->token,
729 somelock->comment, 0, 0,
730 SVN_INVALID_REVNUM,
731 TRUE /* FORCE STEAL */,
732 pool));
733 SVN_ERR(svn_fs_get_lock(&somelock, fs, "/A/D/G/rho", pool));
734 if (somelock->expiration_date)
735 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
736 "Made non-expirirng lock, but it expires.");
737
738 return SVN_NO_ERROR;
739 }
740
741
742 /* Test that svn_fs_lock() and svn_fs_attach_lock() can do
743 out-of-dateness checks.. */
744 static svn_error_t *
lock_out_of_date(const svn_test_opts_t * opts,apr_pool_t * pool)745 lock_out_of_date(const svn_test_opts_t *opts,
746 apr_pool_t *pool)
747 {
748 svn_fs_t *fs;
749 svn_fs_txn_t *txn;
750 svn_fs_root_t *txn_root;
751 const char *conflict;
752 svn_revnum_t newrev;
753 svn_fs_access_t *access;
754 svn_lock_t *mylock;
755 svn_error_t *err;
756
757 SVN_ERR(create_greek_fs(&fs, &newrev, "test-repo-lock-out-of-date",
758 opts, pool));
759
760 /* Commit a small change to /A/D/G/rho, creating revision 2. */
761 SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
762 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
763 SVN_ERR(svn_test__set_file_contents(txn_root, "/A/D/G/rho",
764 "new contents", pool));
765 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
766 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(newrev));
767
768 /* We are now 'bubba'. */
769 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
770 SVN_ERR(svn_fs_set_access(fs, access));
771
772 /* Try to lock /A/D/G/rho, but claim that we still have r1 of the file. */
773 err = svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0, 0, 1, FALSE, pool);
774 if (! err)
775 return svn_error_create
776 (SVN_ERR_TEST_FAILED, NULL,
777 "Uhoh, able to lock an out-of-date file.");
778 svn_error_clear(err);
779
780 /* Attempt lock again, this time claiming to have r2. */
781 SVN_ERR(svn_fs_lock(&mylock, fs, "/A/D/G/rho", NULL, "", 0,
782 0, 2, FALSE, pool));
783
784 /* 'Refresh' the lock, claiming to have r1... should fail. */
785 err = svn_fs_lock(&mylock, fs, mylock->path,
786 mylock->token, mylock->comment, 0,
787 apr_time_now() + apr_time_from_sec(50),
788 1,
789 TRUE /* FORCE STEAL */,
790 pool);
791 if (! err)
792 return svn_error_create
793 (SVN_ERR_TEST_FAILED, NULL,
794 "Uhoh, able to refresh a lock on an out-of-date file.");
795 svn_error_clear(err);
796
797 return SVN_NO_ERROR;
798 }
799
800 struct lock_result_t {
801 const svn_lock_t *lock;
802 svn_error_t *fs_err;
803 };
804
805 static svn_error_t *
expect_lock(const char * path,apr_hash_t * results,svn_fs_t * fs,apr_pool_t * scratch_pool)806 expect_lock(const char *path,
807 apr_hash_t *results,
808 svn_fs_t *fs,
809 apr_pool_t *scratch_pool)
810 {
811 svn_lock_t *lock;
812 struct lock_result_t *result = svn_hash_gets(results, path);
813
814 SVN_TEST_ASSERT(result && result->lock && !result->fs_err);
815 SVN_ERR(svn_fs_get_lock(&lock, fs, path, scratch_pool));
816 SVN_TEST_ASSERT(lock);
817 return SVN_NO_ERROR;
818 }
819
820 static svn_error_t *
expect_error(const char * path,apr_hash_t * results,svn_fs_t * fs,apr_pool_t * scratch_pool)821 expect_error(const char *path,
822 apr_hash_t *results,
823 svn_fs_t *fs,
824 apr_pool_t *scratch_pool)
825 {
826 svn_lock_t *lock;
827 struct lock_result_t *result = svn_hash_gets(results, path);
828
829 SVN_TEST_ASSERT(result && !result->lock && result->fs_err);
830 svn_error_clear(result->fs_err);
831 SVN_ERR(svn_fs_get_lock(&lock, fs, path, scratch_pool));
832 SVN_TEST_ASSERT(!lock);
833 return SVN_NO_ERROR;
834 }
835
836 static svn_error_t *
expect_unlock(const char * path,apr_hash_t * results,svn_fs_t * fs,apr_pool_t * scratch_pool)837 expect_unlock(const char *path,
838 apr_hash_t *results,
839 svn_fs_t *fs,
840 apr_pool_t *scratch_pool)
841 {
842 svn_lock_t *lock;
843 struct lock_result_t *result = svn_hash_gets(results, path);
844
845 SVN_TEST_ASSERT(result && !result->fs_err);
846 SVN_ERR(svn_fs_get_lock(&lock, fs, path, scratch_pool));
847 SVN_TEST_ASSERT(!lock);
848 return SVN_NO_ERROR;
849 }
850
851 static svn_error_t *
expect_unlock_error(const char * path,apr_hash_t * results,svn_fs_t * fs,apr_pool_t * scratch_pool)852 expect_unlock_error(const char *path,
853 apr_hash_t *results,
854 svn_fs_t *fs,
855 apr_pool_t *scratch_pool)
856 {
857 svn_lock_t *lock;
858 struct lock_result_t *result = svn_hash_gets(results, path);
859
860 SVN_TEST_ASSERT(result && result->fs_err);
861 svn_error_clear(result->fs_err);
862 SVN_ERR(svn_fs_get_lock(&lock, fs, path, scratch_pool));
863 SVN_TEST_ASSERT(lock);
864 return SVN_NO_ERROR;
865 }
866
867 struct lock_many_baton_t {
868 apr_hash_t *results;
869 apr_pool_t *pool;
870 int count;
871 };
872
873 /* Implements svn_fs_lock_callback_t. */
874 static svn_error_t *
lock_many_cb(void * lock_baton,const char * path,const svn_lock_t * lock,svn_error_t * fs_err,apr_pool_t * pool)875 lock_many_cb(void *lock_baton,
876 const char *path,
877 const svn_lock_t *lock,
878 svn_error_t *fs_err,
879 apr_pool_t *pool)
880 {
881 struct lock_many_baton_t *b = lock_baton;
882 struct lock_result_t *result = apr_palloc(b->pool,
883 sizeof(struct lock_result_t));
884
885 result->lock = lock;
886 result->fs_err = svn_error_dup(fs_err);
887 svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result);
888
889 if (b->count)
890 if (!--(b->count))
891 return svn_error_create(SVN_ERR_FS_GENERAL, NULL, "lock_many_cb");
892
893 return SVN_NO_ERROR;
894 }
895
896 static svn_error_t *
lock_multiple_paths(const svn_test_opts_t * opts,apr_pool_t * pool)897 lock_multiple_paths(const svn_test_opts_t *opts,
898 apr_pool_t *pool)
899 {
900 svn_fs_t *fs;
901 svn_fs_txn_t *txn;
902 svn_fs_root_t *root, *txn_root;
903 const char *conflict;
904 svn_revnum_t newrev;
905 svn_fs_access_t *access;
906 svn_fs_lock_target_t *target;
907 struct lock_many_baton_t baton;
908 apr_hash_t *lock_paths, *unlock_paths;
909 apr_hash_index_t *hi;
910
911 SVN_ERR(create_greek_fs(&fs, &newrev, "test-lock-multiple-paths",
912 opts, pool));
913 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
914 SVN_ERR(svn_fs_set_access(fs, access));
915 SVN_ERR(svn_fs_revision_root(&root, fs, newrev, pool));
916 SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
917 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
918 SVN_ERR(svn_fs_make_dir(txn_root, "/A/BB", pool));
919 SVN_ERR(svn_fs_make_dir(txn_root, "/A/BBB", pool));
920 SVN_ERR(svn_fs_copy(root, "/A/mu", txn_root, "/A/BB/mu", pool));
921 SVN_ERR(svn_fs_copy(root, "/A/mu", txn_root, "/A/BBB/mu", pool));
922 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
923
924 baton.results = apr_hash_make(pool);
925 baton.pool = pool;
926 baton.count = 0;
927 lock_paths = apr_hash_make(pool);
928 unlock_paths = apr_hash_make(pool);
929 target = svn_fs_lock_target_create(NULL, newrev, pool);
930
931 svn_hash_sets(lock_paths, "/A/B/E/alpha", target);
932 svn_hash_sets(lock_paths, "/A/B/E/beta", target);
933 svn_hash_sets(lock_paths, "/A/B/E/zulu", target);
934 svn_hash_sets(lock_paths, "/A/BB/mu", target);
935 svn_hash_sets(lock_paths, "/A/BBB/mu", target);
936 svn_hash_sets(lock_paths, "/A/D/G/pi", target);
937 svn_hash_sets(lock_paths, "/A/D/G/rho", target);
938 svn_hash_sets(lock_paths, "/A/mu", target);
939 svn_hash_sets(lock_paths, "/X/zulu", target);
940
941 /* Lock some paths. */
942 apr_hash_clear(baton.results);
943 SVN_ERR(svn_fs_lock_many(fs, lock_paths, "comment", 0, 0, 0,
944 lock_many_cb, &baton,
945 pool, pool));
946
947 SVN_ERR(expect_lock("/A/B/E/alpha", baton.results, fs, pool));
948 SVN_ERR(expect_lock("/A/B/E/beta", baton.results, fs, pool));
949 SVN_ERR(expect_error("/A/B/E/zulu", baton.results, fs, pool));
950 SVN_ERR(expect_lock("/A/BB/mu", baton.results, fs, pool));
951 SVN_ERR(expect_lock("/A/BBB/mu", baton.results, fs, pool));
952 SVN_ERR(expect_lock("/A/D/G/pi", baton.results, fs, pool));
953 SVN_ERR(expect_lock("/A/D/G/rho", baton.results, fs, pool));
954 SVN_ERR(expect_lock("/A/mu", baton.results, fs, pool));
955 SVN_ERR(expect_error("/X/zulu", baton.results, fs, pool));
956
957 /* Unlock without force and wrong tokens. */
958 for (hi = apr_hash_first(pool, lock_paths); hi; hi = apr_hash_next(hi))
959 svn_hash_sets(unlock_paths, apr_hash_this_key(hi), "wrong-token");
960 apr_hash_clear(baton.results);
961 SVN_ERR(svn_fs_unlock_many(fs, unlock_paths, FALSE, lock_many_cb, &baton,
962 pool, pool));
963
964 SVN_ERR(expect_unlock_error("/A/B/E/alpha", baton.results, fs, pool));
965 SVN_ERR(expect_unlock_error("/A/B/E/beta", baton.results, fs, pool));
966 SVN_ERR(expect_error("/A/B/E/zulu", baton.results, fs, pool));
967 SVN_ERR(expect_unlock_error("/A/BB/mu", baton.results, fs, pool));
968 SVN_ERR(expect_unlock_error("/A/BBB/mu", baton.results, fs, pool));
969 SVN_ERR(expect_unlock_error("/A/D/G/pi", baton.results, fs, pool));
970 SVN_ERR(expect_unlock_error("/A/D/G/rho", baton.results, fs, pool));
971 SVN_ERR(expect_unlock_error("/A/mu", baton.results, fs, pool));
972 SVN_ERR(expect_error("/X/zulu", baton.results, fs, pool));
973
974 /* Force unlock. */
975 for (hi = apr_hash_first(pool, lock_paths); hi; hi = apr_hash_next(hi))
976 svn_hash_sets(unlock_paths, apr_hash_this_key(hi), "");
977 apr_hash_clear(baton.results);
978 SVN_ERR(svn_fs_unlock_many(fs, unlock_paths, TRUE, lock_many_cb, &baton,
979 pool, pool));
980
981 SVN_ERR(expect_unlock("/A/B/E/alpha", baton.results, fs, pool));
982 SVN_ERR(expect_unlock("/A/B/E/beta", baton.results, fs, pool));
983 SVN_ERR(expect_error("/A/B/E/zulu", baton.results, fs, pool));
984 SVN_ERR(expect_unlock("/A/BB/mu", baton.results, fs, pool));
985 SVN_ERR(expect_unlock("/A/BBB/mu", baton.results, fs, pool));
986 SVN_ERR(expect_unlock("/A/D/G/pi", baton.results, fs, pool));
987 SVN_ERR(expect_unlock("/A/D/G/rho", baton.results, fs, pool));
988 SVN_ERR(expect_unlock("/A/mu", baton.results, fs, pool));
989 SVN_ERR(expect_error("/X/zulu", baton.results, fs, pool));
990
991 /* Lock again. */
992 apr_hash_clear(baton.results);
993 SVN_ERR(svn_fs_lock_many(fs, lock_paths, "comment", 0, 0, 0,
994 lock_many_cb, &baton,
995 pool, pool));
996
997 SVN_ERR(expect_lock("/A/B/E/alpha", baton.results, fs, pool));
998 SVN_ERR(expect_lock("/A/B/E/beta", baton.results, fs, pool));
999 SVN_ERR(expect_error("/A/B/E/zulu", baton.results, fs, pool));
1000 SVN_ERR(expect_lock("/A/BB/mu", baton.results, fs, pool));
1001 SVN_ERR(expect_lock("/A/BBB/mu", baton.results, fs, pool));
1002 SVN_ERR(expect_lock("/A/D/G/pi", baton.results, fs, pool));
1003 SVN_ERR(expect_lock("/A/D/G/rho", baton.results, fs, pool));
1004 SVN_ERR(expect_lock("/A/mu", baton.results, fs, pool));
1005 SVN_ERR(expect_error("/X/zulu", baton.results, fs, pool));
1006
1007 /* Unlock without force. */
1008 for (hi = apr_hash_first(pool, baton.results); hi; hi = apr_hash_next(hi))
1009 {
1010 struct lock_result_t *result = apr_hash_this_val(hi);
1011 svn_hash_sets(unlock_paths, apr_hash_this_key(hi),
1012 result->lock ? result->lock->token : "non-existent-token");
1013 }
1014 apr_hash_clear(baton.results);
1015 SVN_ERR(svn_fs_unlock_many(fs, unlock_paths, FALSE, lock_many_cb, &baton,
1016 pool, pool));
1017
1018 SVN_ERR(expect_unlock("/A/B/E/alpha", baton.results, fs, pool));
1019 SVN_ERR(expect_unlock("/A/B/E/beta", baton.results, fs, pool));
1020 SVN_ERR(expect_error("/A/B/E/zulu", baton.results, fs, pool));
1021 SVN_ERR(expect_unlock("/A/BB/mu", baton.results, fs, pool));
1022 SVN_ERR(expect_unlock("/A/BBB/mu", baton.results, fs, pool));
1023 SVN_ERR(expect_unlock("/A/D/G/pi", baton.results, fs, pool));
1024 SVN_ERR(expect_unlock("/A/D/G/rho", baton.results, fs, pool));
1025 SVN_ERR(expect_unlock("/A/mu", baton.results, fs, pool));
1026 SVN_ERR(expect_error("/X/zulu", baton.results, fs, pool));
1027
1028 return SVN_NO_ERROR;
1029 }
1030
1031 static svn_error_t *
lock_cb_error(const svn_test_opts_t * opts,apr_pool_t * pool)1032 lock_cb_error(const svn_test_opts_t *opts,
1033 apr_pool_t *pool)
1034 {
1035 svn_fs_t *fs;
1036 svn_revnum_t newrev;
1037 svn_fs_access_t *access;
1038 svn_fs_lock_target_t *target;
1039 struct lock_many_baton_t baton;
1040 apr_hash_t *lock_paths, *unlock_paths;
1041 svn_lock_t *lock;
1042
1043 SVN_ERR(create_greek_fs(&fs, &newrev, "test-lock-cb-error", opts, pool));
1044 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
1045 SVN_ERR(svn_fs_set_access(fs, access));
1046
1047 baton.results = apr_hash_make(pool);
1048 baton.pool = pool;
1049 baton.count = 1;
1050 lock_paths = apr_hash_make(pool);
1051 unlock_paths = apr_hash_make(pool);
1052 target = svn_fs_lock_target_create(NULL, newrev, pool);
1053
1054 svn_hash_sets(lock_paths, "/A/B/E/alpha", target);
1055 svn_hash_sets(lock_paths, "/A/B/E/beta", target);
1056
1057 apr_hash_clear(baton.results);
1058 SVN_TEST_ASSERT_ERROR(svn_fs_lock_many(fs, lock_paths, "comment", 0, 0, 0,
1059 lock_many_cb, &baton,
1060 pool, pool),
1061 SVN_ERR_FS_GENERAL);
1062
1063 SVN_TEST_ASSERT(apr_hash_count(baton.results) == 1);
1064 SVN_TEST_ASSERT(svn_hash_gets(baton.results, "/A/B/E/alpha")
1065 || svn_hash_gets(baton.results, "/A/B/E/beta"));
1066 SVN_ERR(svn_fs_get_lock(&lock, fs, "/A/B/E/alpha", pool));
1067 SVN_TEST_ASSERT(lock);
1068 svn_hash_sets(unlock_paths, "/A/B/E/alpha", lock->token);
1069 SVN_ERR(svn_fs_get_lock(&lock, fs, "/A/B/E/beta", pool));
1070 SVN_TEST_ASSERT(lock);
1071 svn_hash_sets(unlock_paths, "/A/B/E/beta", lock->token);
1072
1073 baton.count = 1;
1074 apr_hash_clear(baton.results);
1075 SVN_TEST_ASSERT_ERROR(svn_fs_unlock_many(fs, unlock_paths, FALSE,
1076 lock_many_cb, &baton,
1077 pool, pool),
1078 SVN_ERR_FS_GENERAL);
1079
1080 SVN_TEST_ASSERT(apr_hash_count(baton.results) == 1);
1081 SVN_TEST_ASSERT(svn_hash_gets(baton.results, "/A/B/E/alpha")
1082 || svn_hash_gets(baton.results, "/A/B/E/beta"));
1083
1084 SVN_ERR(svn_fs_get_lock(&lock, fs, "/A/B/E/alpha", pool));
1085 SVN_TEST_ASSERT(!lock);
1086 SVN_ERR(svn_fs_get_lock(&lock, fs, "/A/B/E/beta", pool));
1087 SVN_TEST_ASSERT(!lock);
1088
1089 return SVN_NO_ERROR;
1090 }
1091
1092 /* XXX NOTE:
1093 This test will fail on most Unix-like systems when run as the
1094 root user, because flock() will ignore file permissions. */
1095 static svn_error_t *
obtain_write_lock_failure(const svn_test_opts_t * opts,apr_pool_t * pool)1096 obtain_write_lock_failure(const svn_test_opts_t *opts,
1097 apr_pool_t *pool)
1098 {
1099 svn_fs_t *fs;
1100 svn_revnum_t newrev;
1101 svn_fs_access_t *access;
1102 svn_fs_lock_target_t *target;
1103 struct lock_many_baton_t baton;
1104 apr_hash_t *lock_paths, *unlock_paths;
1105
1106 /* The test makes sense only for FSFS. */
1107 if (strcmp(opts->fs_type, SVN_FS_TYPE_FSFS) != 0
1108 && strcmp(opts->fs_type, SVN_FS_TYPE_FSX) != 0)
1109 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
1110 "this will test FSFS/FSX repositories only");
1111
1112 SVN_ERR(create_greek_fs(&fs, &newrev, "test-obtain-write-lock-failure",
1113 opts, pool));
1114 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
1115 SVN_ERR(svn_fs_set_access(fs, access));
1116
1117 /* Make a read only 'write-lock' file. This prevents any write operations
1118 from being executed. */
1119 SVN_ERR(svn_io_set_file_read_only("test-obtain-write-lock-failure/write-lock",
1120 FALSE, pool));
1121
1122 baton.results = apr_hash_make(pool);
1123 baton.pool = pool;
1124 baton.count = 0;
1125
1126 /* Trying to lock some paths. We don't really care about error; the test
1127 shouldn't crash. */
1128 target = svn_fs_lock_target_create(NULL, newrev, pool);
1129 lock_paths = apr_hash_make(pool);
1130 svn_hash_sets(lock_paths, "/iota", target);
1131 svn_hash_sets(lock_paths, "/A/mu", target);
1132
1133 apr_hash_clear(baton.results);
1134 SVN_TEST_ASSERT_ANY_ERROR(svn_fs_lock_many(fs, lock_paths, "comment", 0, 0, 0,
1135 lock_many_cb, &baton, pool, pool));
1136
1137 /* Trying to unlock some paths. We don't really care about error; the test
1138 shouldn't crash. */
1139 unlock_paths = apr_hash_make(pool);
1140 svn_hash_sets(unlock_paths, "/iota", "");
1141 svn_hash_sets(unlock_paths, "/A/mu", "");
1142
1143 apr_hash_clear(baton.results);
1144 SVN_TEST_ASSERT_ANY_ERROR(svn_fs_unlock_many(fs, unlock_paths, TRUE,
1145 lock_many_cb, &baton, pool,
1146 pool));
1147
1148 return SVN_NO_ERROR;
1149 }
1150
1151 static svn_error_t *
parent_and_child_lock(const svn_test_opts_t * opts,apr_pool_t * pool)1152 parent_and_child_lock(const svn_test_opts_t *opts,
1153 apr_pool_t *pool)
1154 {
1155 svn_fs_t *fs;
1156 svn_fs_access_t *access;
1157 svn_fs_txn_t *txn;
1158 svn_fs_root_t *root;
1159 const char *conflict;
1160 svn_revnum_t newrev;
1161 svn_lock_t *lock;
1162 struct get_locks_baton_t *get_locks_baton;
1163 apr_size_t num_expected_paths;
1164
1165 SVN_ERR(svn_test__create_fs(&fs, "test-parent-and-child-lock", opts, pool));
1166 SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
1167 SVN_ERR(svn_fs_set_access(fs, access));
1168
1169 /* Make a file '/A'. */
1170 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
1171 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
1172 SVN_ERR(svn_fs_make_file(root, "/A", pool));
1173 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
1174
1175 /* Obtain a lock on '/A'. */
1176 SVN_ERR(svn_fs_lock(&lock, fs, "/A", NULL, NULL, FALSE, 0, newrev, FALSE,
1177 pool));
1178
1179 /* Add a lock token to FS access context. */
1180 SVN_ERR(svn_fs_access_add_lock_token(access, lock->token));
1181
1182 /* Make some weird change: replace file '/A' by a directory with a
1183 child. Issue 2507 means that the result is that the directory /A
1184 remains locked. */
1185 SVN_ERR(svn_fs_begin_txn(&txn, fs, newrev, pool));
1186 SVN_ERR(svn_fs_txn_root(&root, txn, pool));
1187 SVN_ERR(svn_fs_delete(root, "/A", pool));
1188 SVN_ERR(svn_fs_make_dir(root, "/A", pool));
1189 SVN_ERR(svn_fs_make_file(root, "/A/b", pool));
1190 SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
1191
1192 /* Obtain a lock on '/A/b'. Issue 2507 means that the lock index
1193 for / refers to both /A and /A/b, and that the lock index for /A
1194 refers to /A/b. */
1195 SVN_ERR(svn_fs_lock(&lock, fs, "/A/b", NULL, NULL, FALSE, 0, newrev, FALSE,
1196 pool));
1197
1198 /* Verify the locked paths. The lock for /A/b should not be reported
1199 twice even though issue 2507 means we access the index for / and
1200 the index for /A both of which refer to /A/b. */
1201 {
1202 static const char *expected_paths[] = {
1203 "/A",
1204 "/A/b",
1205 };
1206 num_expected_paths = sizeof(expected_paths) / sizeof(const char *);
1207 get_locks_baton = make_get_locks_baton(pool);
1208 SVN_ERR(svn_fs_get_locks(fs, "/", get_locks_callback,
1209 get_locks_baton, pool));
1210 SVN_ERR(verify_matching_lock_paths(get_locks_baton, expected_paths,
1211 num_expected_paths, pool));
1212 }
1213
1214 return SVN_NO_ERROR;
1215 }
1216
1217 /* ------------------------------------------------------------------------ */
1218
1219 /* The test table. */
1220
1221 static int max_threads = 2;
1222
1223 static struct svn_test_descriptor_t test_funcs[] =
1224 {
1225 SVN_TEST_NULL,
1226 SVN_TEST_OPTS_PASS(lock_expiration,
1227 "test that locks can expire"),
1228 SVN_TEST_OPTS_PASS(lock_only,
1229 "lock only"),
1230 SVN_TEST_OPTS_PASS(lookup_lock_by_path,
1231 "lookup lock by path"),
1232 SVN_TEST_OPTS_PASS(attach_lock,
1233 "attach lock"),
1234 SVN_TEST_OPTS_PASS(get_locks,
1235 "get locks"),
1236 SVN_TEST_OPTS_PASS(basic_lock,
1237 "basic locking"),
1238 SVN_TEST_OPTS_PASS(lock_credentials,
1239 "test that locking requires proper credentials"),
1240 SVN_TEST_OPTS_PASS(final_lock_check,
1241 "test that locking is enforced in final commit step"),
1242 SVN_TEST_OPTS_PASS(lock_dir_propchange,
1243 "dir propchange can be committed with locked child"),
1244 SVN_TEST_OPTS_PASS(lock_break_steal_refresh,
1245 "breaking, stealing, refreshing a lock"),
1246 SVN_TEST_OPTS_PASS(lock_out_of_date,
1247 "check out-of-dateness before locking"),
1248 SVN_TEST_OPTS_PASS(lock_multiple_paths,
1249 "lock multiple paths"),
1250 SVN_TEST_OPTS_PASS(lock_cb_error,
1251 "lock callback error"),
1252 SVN_TEST_OPTS_PASS(obtain_write_lock_failure,
1253 "lock/unlock when 'write-lock' couldn't be obtained"),
1254 SVN_TEST_OPTS_PASS(parent_and_child_lock,
1255 "lock parent and it's child"),
1256 SVN_TEST_NULL
1257 };
1258
1259 SVN_TEST_MAIN
1260