1 /* fs-x-pack-test.c --- tests for the FSX filesystem
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 <stdlib.h>
24 #include <string.h>
25 #include <apr_pools.h>
26
27 #include "../svn_test.h"
28 #include "../../libsvn_fs_x/batch_fsync.h"
29 #include "../../libsvn_fs_x/fs.h"
30 #include "../../libsvn_fs_x/reps.h"
31
32 #include "svn_pools.h"
33 #include "svn_props.h"
34 #include "svn_fs.h"
35 #include "private/svn_string_private.h"
36
37 #include "../svn_test_fs.h"
38
39
40
41 /*** Helper Functions ***/
42
43 /* Write the format number and maximum number of files per directory
44 to a new format file in PATH, overwriting a previously existing
45 file. Use POOL for temporary allocation.
46
47 (This implementation is largely stolen from libsvn_fs_fs/fs_fs.c.) */
48 static svn_error_t *
write_format(const char * path,int format,int max_files_per_dir,apr_pool_t * pool)49 write_format(const char *path,
50 int format,
51 int max_files_per_dir,
52 apr_pool_t *pool)
53 {
54 const char *contents;
55
56 path = svn_dirent_join(path, "format", pool);
57 SVN_TEST_ASSERT(max_files_per_dir > 0);
58
59 contents = apr_psprintf(pool,
60 "%d\n"
61 "layout sharded %d\n",
62 format, max_files_per_dir);
63
64 SVN_ERR(svn_io_write_atomic2(path, contents, strlen(contents),
65 NULL /* copy perms */, FALSE, pool));
66
67 /* And set the perms to make it read only */
68 return svn_io_set_file_read_only(path, FALSE, pool);
69 }
70
71 /* Return the expected contents of "iota" in revision REV. */
72 static const char *
get_rev_contents(svn_revnum_t rev,apr_pool_t * pool)73 get_rev_contents(svn_revnum_t rev, apr_pool_t *pool)
74 {
75 /* Toss in a bunch of magic numbers for spice. */
76 apr_int64_t num = ((rev * 1234353 + 4358) * 4583 + ((rev % 4) << 1)) / 42;
77 return apr_psprintf(pool, "%" APR_INT64_T_FMT "\n", num);
78 }
79
80 struct pack_notify_baton
81 {
82 apr_int64_t expected_shard;
83 svn_fs_pack_notify_action_t expected_action;
84 };
85
86 static svn_error_t *
pack_notify(void * baton,apr_int64_t shard,svn_fs_pack_notify_action_t action,apr_pool_t * pool)87 pack_notify(void *baton,
88 apr_int64_t shard,
89 svn_fs_pack_notify_action_t action,
90 apr_pool_t *pool)
91 {
92 struct pack_notify_baton *pnb = baton;
93
94 SVN_TEST_ASSERT(shard == pnb->expected_shard);
95 SVN_TEST_ASSERT(action == pnb->expected_action);
96
97 /* Update expectations. */
98 switch (action)
99 {
100 case svn_fs_pack_notify_start:
101 pnb->expected_action = svn_fs_pack_notify_end;
102 break;
103
104 case svn_fs_pack_notify_end:
105 pnb->expected_action = svn_fs_pack_notify_start;
106 pnb->expected_shard++;
107 break;
108
109 default:
110 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
111 "Unknown notification action when packing");
112 }
113
114 return SVN_NO_ERROR;
115 }
116
117 #define R1_LOG_MSG "Let's serf"
118
119 /* Create a packed filesystem in DIR. Set the shard size to
120 SHARD_SIZE and create NUM_REVS number of revisions (in addition to
121 r0). Use POOL for allocations. After this function successfully
122 completes, the filesystem's youngest revision number will be the
123 same as NUM_REVS. */
124 static svn_error_t *
create_packed_filesystem(const char * dir,const svn_test_opts_t * opts,int num_revs,int shard_size,apr_pool_t * pool)125 create_packed_filesystem(const char *dir,
126 const svn_test_opts_t *opts,
127 int num_revs,
128 int shard_size,
129 apr_pool_t *pool)
130 {
131 svn_fs_t *fs;
132 svn_fs_txn_t *txn;
133 svn_fs_root_t *txn_root;
134 const char *conflict;
135 svn_revnum_t after_rev;
136 apr_pool_t *subpool = svn_pool_create(pool);
137 struct pack_notify_baton pnb;
138 apr_pool_t *iterpool;
139 int version;
140
141 /* Bail (with success) on known-untestable scenarios */
142 if (strcmp(opts->fs_type, "fsx") != 0)
143 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
144 "this will test FSX repositories only");
145
146 if (opts->server_minor_version && (opts->server_minor_version < 9))
147 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
148 "pre-1.9 SVN doesn't support FSX");
149
150 /* Create a filesystem, then close it */
151 SVN_ERR(svn_test__create_fs(&fs, dir, opts, subpool));
152 svn_pool_destroy(subpool);
153
154 subpool = svn_pool_create(pool);
155
156 /* Rewrite the format file */
157 SVN_ERR(svn_io_read_version_file(&version,
158 svn_dirent_join(dir, "format", subpool),
159 subpool));
160 SVN_ERR(write_format(dir, version, shard_size, subpool));
161
162 /* Reopen the filesystem */
163 SVN_ERR(svn_fs_open2(&fs, dir, NULL, subpool, subpool));
164
165 /* Revision 1: the Greek tree */
166 SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
167 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
168 SVN_ERR(svn_test__create_greek_tree(txn_root, subpool));
169 SVN_ERR(svn_fs_change_txn_prop(txn, SVN_PROP_REVISION_LOG,
170 svn_string_create(R1_LOG_MSG, pool),
171 pool));
172 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
173 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
174
175 /* Revisions 2 thru NUM_REVS-1: content tweaks to "iota". */
176 iterpool = svn_pool_create(subpool);
177 while (after_rev < num_revs)
178 {
179 svn_pool_clear(iterpool);
180 SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, iterpool));
181 SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
182 SVN_ERR(svn_test__set_file_contents(txn_root, "iota",
183 get_rev_contents(after_rev + 1,
184 iterpool),
185 iterpool));
186 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, iterpool));
187 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
188 }
189 svn_pool_destroy(iterpool);
190 svn_pool_destroy(subpool);
191
192 /* Now pack the FS */
193 pnb.expected_shard = 0;
194 pnb.expected_action = svn_fs_pack_notify_start;
195 return svn_fs_pack(dir, pack_notify, &pnb, NULL, NULL, pool);
196 }
197
198 /* Create a packed FSFS filesystem for revprop tests at REPO_NAME with
199 * MAX_REV revisions and the given SHARD_SIZE and OPTS. Return it in *FS.
200 * Use POOL for allocations.
201 */
202 static svn_error_t *
prepare_revprop_repo(svn_fs_t ** fs,const char * repo_name,int max_rev,int shard_size,const svn_test_opts_t * opts,apr_pool_t * pool)203 prepare_revprop_repo(svn_fs_t **fs,
204 const char *repo_name,
205 int max_rev,
206 int shard_size,
207 const svn_test_opts_t *opts,
208 apr_pool_t *pool)
209 {
210 svn_fs_txn_t *txn;
211 svn_fs_root_t *txn_root;
212 const char *conflict;
213 svn_revnum_t after_rev;
214 apr_pool_t *subpool;
215
216 /* Create the packed FS and open it. */
217 SVN_ERR(create_packed_filesystem(repo_name, opts, max_rev, shard_size, pool));
218 SVN_ERR(svn_fs_open2(fs, repo_name, NULL, pool, pool));
219
220 subpool = svn_pool_create(pool);
221 /* Do a commit to trigger packing. */
222 SVN_ERR(svn_fs_begin_txn(&txn, *fs, max_rev, subpool));
223 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
224 SVN_ERR(svn_test__set_file_contents(txn_root, "iota", "new-iota", subpool));
225 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
226 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
227 svn_pool_destroy(subpool);
228
229 /* Pack the repository. */
230 SVN_ERR(svn_fs_pack(repo_name, NULL, NULL, NULL, NULL, pool));
231
232 return SVN_NO_ERROR;
233 }
234
235 /* For revision REV, return a short log message allocated in POOL.
236 */
237 static svn_string_t *
default_log(svn_revnum_t rev,apr_pool_t * pool)238 default_log(svn_revnum_t rev, apr_pool_t *pool)
239 {
240 return svn_string_createf(pool, "Default message for rev %ld", rev);
241 }
242
243 /* For revision REV, return a long log message allocated in POOL.
244 */
245 static svn_string_t *
large_log(svn_revnum_t rev,apr_size_t length,apr_pool_t * pool)246 large_log(svn_revnum_t rev, apr_size_t length, apr_pool_t *pool)
247 {
248 svn_stringbuf_t *temp = svn_stringbuf_create_ensure(100000, pool);
249 int i, count = (int)(length - 50) / 6;
250
251 svn_stringbuf_appendcstr(temp, "A ");
252 for (i = 0; i < count; ++i)
253 svn_stringbuf_appendcstr(temp, "very, ");
254
255 svn_stringbuf_appendcstr(temp,
256 apr_psprintf(pool, "very long message for rev %ld, indeed", rev));
257
258 return svn_stringbuf__morph_into_string(temp);
259 }
260
261 /* For revision REV, return a long log message allocated in POOL.
262 */
263 static svn_string_t *
huge_log(svn_revnum_t rev,apr_pool_t * pool)264 huge_log(svn_revnum_t rev, apr_pool_t *pool)
265 {
266 return large_log(rev, 90000, pool);
267 }
268
269
270 /*** Tests ***/
271
272 /* ------------------------------------------------------------------------ */
273 #define REPO_NAME "test-repo-fsx-pack"
274 #define SHARD_SIZE 7
275 #define MAX_REV 53
276 static svn_error_t *
pack_filesystem(const svn_test_opts_t * opts,apr_pool_t * pool)277 pack_filesystem(const svn_test_opts_t *opts,
278 apr_pool_t *pool)
279 {
280 int i;
281 svn_node_kind_t kind;
282 const char *path;
283 char buf[80];
284 apr_file_t *file;
285 apr_size_t len;
286
287 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
288 pool));
289
290 /* Check to see that the pack files exist, and that the rev directories
291 don't. */
292 for (i = 0; i < (MAX_REV + 1) / SHARD_SIZE; i++)
293 {
294 path = svn_dirent_join_many(pool, REPO_NAME, "revs",
295 apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
296 "pack", SVN_VA_NULL);
297
298 /* This file should exist. */
299 SVN_ERR(svn_io_check_path(path, &kind, pool));
300 if (kind != svn_node_file)
301 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
302 "Expected pack file '%s' not found", path);
303
304 /* This directory should not exist. */
305 path = svn_dirent_join_many(pool, REPO_NAME, "revs",
306 apr_psprintf(pool, "%d", i / SHARD_SIZE),
307 SVN_VA_NULL);
308 SVN_ERR(svn_io_check_path(path, &kind, pool));
309 if (kind != svn_node_none)
310 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
311 "Unexpected directory '%s' found", path);
312 }
313
314 /* Ensure the min-unpacked-rev jives with the above operations. */
315 SVN_ERR(svn_io_file_open(&file,
316 svn_dirent_join(REPO_NAME, PATH_MIN_UNPACKED_REV,
317 pool),
318 APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
319 len = sizeof(buf);
320 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
321 SVN_ERR(svn_io_file_close(file, pool));
322 if (SVN_STR_TO_REV(buf) != (MAX_REV / SHARD_SIZE) * SHARD_SIZE)
323 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
324 "Bad '%s' contents", PATH_MIN_UNPACKED_REV);
325
326 /* Finally, make sure the final revision directory does exist. */
327 path = svn_dirent_join_many(pool, REPO_NAME, "revs",
328 apr_psprintf(pool, "%d", (i / SHARD_SIZE) + 1),
329 SVN_VA_NULL);
330 SVN_ERR(svn_io_check_path(path, &kind, pool));
331 if (kind != svn_node_none)
332 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
333 "Expected directory '%s' not found", path);
334
335
336 return SVN_NO_ERROR;
337 }
338 #undef REPO_NAME
339 #undef SHARD_SIZE
340 #undef MAX_REV
341
342 /* ------------------------------------------------------------------------ */
343 #define REPO_NAME "test-repo-fsx-pack-even"
344 #define SHARD_SIZE 4
345 #define MAX_REV 11
346 static svn_error_t *
pack_even_filesystem(const svn_test_opts_t * opts,apr_pool_t * pool)347 pack_even_filesystem(const svn_test_opts_t *opts,
348 apr_pool_t *pool)
349 {
350 svn_node_kind_t kind;
351 const char *path;
352
353 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
354 pool));
355
356 path = svn_dirent_join_many(pool, REPO_NAME, "revs", "2.pack", SVN_VA_NULL);
357 SVN_ERR(svn_io_check_path(path, &kind, pool));
358 if (kind != svn_node_dir)
359 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
360 "Packing did not complete as expected");
361
362 return SVN_NO_ERROR;
363 }
364 #undef REPO_NAME
365 #undef SHARD_SIZE
366 #undef MAX_REV
367
368 /* ------------------------------------------------------------------------ */
369 #define REPO_NAME "test-repo-read-packed-fs"
370 #define SHARD_SIZE 5
371 #define MAX_REV 11
372 static svn_error_t *
read_packed_fs(const svn_test_opts_t * opts,apr_pool_t * pool)373 read_packed_fs(const svn_test_opts_t *opts,
374 apr_pool_t *pool)
375 {
376 svn_fs_t *fs;
377 svn_stream_t *rstream;
378 svn_stringbuf_t *rstring;
379 svn_revnum_t i;
380
381 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool));
382 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
383
384 for (i = 1; i < (MAX_REV + 1); i++)
385 {
386 svn_fs_root_t *rev_root;
387 svn_stringbuf_t *sb;
388
389 SVN_ERR(svn_fs_revision_root(&rev_root, fs, i, pool));
390 SVN_ERR(svn_fs_file_contents(&rstream, rev_root, "iota", pool));
391 SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool));
392
393 if (i == 1)
394 sb = svn_stringbuf_create("This is the file 'iota'.\n", pool);
395 else
396 sb = svn_stringbuf_create(get_rev_contents(i, pool), pool);
397
398 if (! svn_stringbuf_compare(rstring, sb))
399 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
400 "Bad data in revision %ld.", i);
401 }
402
403 return SVN_NO_ERROR;
404 }
405 #undef REPO_NAME
406 #undef SHARD_SIZE
407 #undef MAX_REV
408
409 /* ------------------------------------------------------------------------ */
410 #define REPO_NAME "test-repo-commit-packed-fs"
411 #define SHARD_SIZE 5
412 #define MAX_REV 10
413 static svn_error_t *
commit_packed_fs(const svn_test_opts_t * opts,apr_pool_t * pool)414 commit_packed_fs(const svn_test_opts_t *opts,
415 apr_pool_t *pool)
416 {
417 svn_fs_t *fs;
418 svn_fs_txn_t *txn;
419 svn_fs_root_t *txn_root;
420 const char *conflict;
421 svn_revnum_t after_rev;
422
423 /* Create the packed FS and open it. */
424 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, 5, pool));
425 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
426
427 /* Now do a commit. */
428 SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, pool));
429 SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
430 SVN_ERR(svn_test__set_file_contents(txn_root, "iota",
431 "How much better is it to get wisdom than gold! and to get "
432 "understanding rather to be chosen than silver!", pool));
433 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, pool));
434 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
435
436 return SVN_NO_ERROR;
437 }
438 #undef REPO_NAME
439 #undef MAX_REV
440 #undef SHARD_SIZE
441
442 /* ------------------------------------------------------------------------ */
443 #define REPO_NAME "test-repo-get-set-revprop-packed-fs"
444 #define SHARD_SIZE 4
445 #define MAX_REV 10
446 static svn_error_t *
get_set_revprop_packed_fs(const svn_test_opts_t * opts,apr_pool_t * pool)447 get_set_revprop_packed_fs(const svn_test_opts_t *opts,
448 apr_pool_t *pool)
449 {
450 svn_fs_t *fs;
451 svn_string_t *prop_value;
452
453 /* Create the packed FS and open it. */
454 SVN_ERR(prepare_revprop_repo(&fs, REPO_NAME, MAX_REV, SHARD_SIZE, opts,
455 pool));
456
457 /* Try to get revprop for revision 0
458 * (non-packed due to special handling). */
459 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 0, SVN_PROP_REVISION_AUTHOR,
460 pool));
461
462 /* Try to change revprop for revision 0
463 * (non-packed due to special handling). */
464 SVN_ERR(svn_fs_change_rev_prop(fs, 0, SVN_PROP_REVISION_AUTHOR,
465 svn_string_create("tweaked-author", pool),
466 pool));
467
468 /* verify */
469 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 0, SVN_PROP_REVISION_AUTHOR,
470 pool));
471 SVN_TEST_STRING_ASSERT(prop_value->data, "tweaked-author");
472
473 /* Try to get packed revprop for revision 5. */
474 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 5, SVN_PROP_REVISION_AUTHOR,
475 pool));
476
477 /* Try to change packed revprop for revision 5. */
478 SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_AUTHOR,
479 svn_string_create("tweaked-author2", pool),
480 pool));
481
482 /* verify */
483 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 5, SVN_PROP_REVISION_AUTHOR,
484 pool));
485 SVN_TEST_STRING_ASSERT(prop_value->data, "tweaked-author2");
486
487 return SVN_NO_ERROR;
488 }
489 #undef REPO_NAME
490 #undef MAX_REV
491 #undef SHARD_SIZE
492
493 /* ------------------------------------------------------------------------ */
494 #define REPO_NAME "test-repo-get-set-large-revprop-packed-fs"
495 #define SHARD_SIZE 4
496 #define MAX_REV 11
497 static svn_error_t *
get_set_large_revprop_packed_fs(const svn_test_opts_t * opts,apr_pool_t * pool)498 get_set_large_revprop_packed_fs(const svn_test_opts_t *opts,
499 apr_pool_t *pool)
500 {
501 svn_fs_t *fs;
502 svn_string_t *prop_value;
503 svn_revnum_t rev;
504
505 /* Create the packed FS and open it. */
506 SVN_ERR(prepare_revprop_repo(&fs, REPO_NAME, MAX_REV, SHARD_SIZE, opts,
507 pool));
508
509 /* Set commit messages to different, large values that fill the pack
510 * files but do not exceed the pack size limit. */
511 for (rev = 0; rev <= MAX_REV; ++rev)
512 SVN_ERR(svn_fs_change_rev_prop(fs, rev, SVN_PROP_REVISION_LOG,
513 large_log(rev, 15000, pool),
514 pool));
515
516 /* verify */
517 for (rev = 0; rev <= MAX_REV; ++rev)
518 {
519 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
520 SVN_PROP_REVISION_LOG, pool));
521 SVN_TEST_STRING_ASSERT(prop_value->data,
522 large_log(rev, 15000, pool)->data);
523 }
524
525 /* Put a larger revprop into the last, some middle and the first revision
526 * of a pack. This should cause the packs to split in the middle. */
527 SVN_ERR(svn_fs_change_rev_prop(fs, 3, SVN_PROP_REVISION_LOG,
528 /* rev 0 is not packed */
529 large_log(3, 37000, pool),
530 pool));
531 SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_LOG,
532 large_log(5, 25000, pool),
533 pool));
534 SVN_ERR(svn_fs_change_rev_prop(fs, 8, SVN_PROP_REVISION_LOG,
535 large_log(8, 25000, pool),
536 pool));
537
538 /* verify */
539 for (rev = 0; rev <= MAX_REV; ++rev)
540 {
541 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
542 SVN_PROP_REVISION_LOG, pool));
543
544 if (rev == 3)
545 SVN_TEST_STRING_ASSERT(prop_value->data,
546 large_log(rev, 37000, pool)->data);
547 else if (rev == 5 || rev == 8)
548 SVN_TEST_STRING_ASSERT(prop_value->data,
549 large_log(rev, 25000, pool)->data);
550 else
551 SVN_TEST_STRING_ASSERT(prop_value->data,
552 large_log(rev, 15000, pool)->data);
553 }
554
555 return SVN_NO_ERROR;
556 }
557 #undef REPO_NAME
558 #undef MAX_REV
559 #undef SHARD_SIZE
560
561 /* ------------------------------------------------------------------------ */
562 #define REPO_NAME "test-repo-get-set-huge-revprop-packed-fs"
563 #define SHARD_SIZE 4
564 #define MAX_REV 10
565 static svn_error_t *
get_set_huge_revprop_packed_fs(const svn_test_opts_t * opts,apr_pool_t * pool)566 get_set_huge_revprop_packed_fs(const svn_test_opts_t *opts,
567 apr_pool_t *pool)
568 {
569 svn_fs_t *fs;
570 svn_string_t *prop_value;
571 svn_revnum_t rev;
572
573 /* Create the packed FS and open it. */
574 SVN_ERR(prepare_revprop_repo(&fs, REPO_NAME, MAX_REV, SHARD_SIZE, opts,
575 pool));
576
577 /* Set commit messages to different values */
578 for (rev = 0; rev <= MAX_REV; ++rev)
579 SVN_ERR(svn_fs_change_rev_prop(fs, rev, SVN_PROP_REVISION_LOG,
580 default_log(rev, pool),
581 pool));
582
583 /* verify */
584 for (rev = 0; rev <= MAX_REV; ++rev)
585 {
586 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
587 SVN_PROP_REVISION_LOG, pool));
588 SVN_TEST_STRING_ASSERT(prop_value->data, default_log(rev, pool)->data);
589 }
590
591 /* Put a huge revprop into the last, some middle and the first revision
592 * of a pack. They will cause the pack files to split accordingly. */
593 SVN_ERR(svn_fs_change_rev_prop(fs, 3, SVN_PROP_REVISION_LOG,
594 huge_log(3, pool),
595 pool));
596 SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_LOG,
597 huge_log(5, pool),
598 pool));
599 SVN_ERR(svn_fs_change_rev_prop(fs, 8, SVN_PROP_REVISION_LOG,
600 huge_log(8, pool),
601 pool));
602
603 /* verify */
604 for (rev = 0; rev <= MAX_REV; ++rev)
605 {
606 SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
607 SVN_PROP_REVISION_LOG, pool));
608
609 if (rev == 3 || rev == 5 || rev == 8)
610 SVN_TEST_STRING_ASSERT(prop_value->data,
611 huge_log(rev, pool)->data);
612 else
613 SVN_TEST_STRING_ASSERT(prop_value->data,
614 default_log(rev, pool)->data);
615 }
616
617 return SVN_NO_ERROR;
618 }
619 #undef REPO_NAME
620 #undef MAX_REV
621 #undef SHARD_SIZE
622
623 /* ------------------------------------------------------------------------ */
624 /* Regression test for issue #3571 (fsfs 'svnadmin recover' expects
625 youngest revprop to be outside revprops.db). */
626 #define REPO_NAME "test-repo-recover-fully-packed"
627 #define SHARD_SIZE 4
628 #define MAX_REV 7
629 static svn_error_t *
recover_fully_packed(const svn_test_opts_t * opts,apr_pool_t * pool)630 recover_fully_packed(const svn_test_opts_t *opts,
631 apr_pool_t *pool)
632 {
633 apr_pool_t *subpool;
634 svn_fs_t *fs;
635 svn_fs_txn_t *txn;
636 svn_fs_root_t *txn_root;
637 const char *conflict;
638 svn_revnum_t after_rev;
639 svn_error_t *err;
640
641 /* Create a packed FS for which every revision will live in a pack
642 digest file, and then recover it. */
643 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool));
644 SVN_ERR(svn_fs_recover(REPO_NAME, NULL, NULL, pool));
645
646 /* Add another revision, re-pack, re-recover. */
647 subpool = svn_pool_create(pool);
648 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, subpool, subpool));
649 SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool));
650 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
651 SVN_ERR(svn_test__set_file_contents(txn_root, "A/mu", "new-mu", subpool));
652 SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
653 SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
654 svn_pool_destroy(subpool);
655 SVN_ERR(svn_fs_pack(REPO_NAME, NULL, NULL, NULL, NULL, pool));
656 SVN_ERR(svn_fs_recover(REPO_NAME, NULL, NULL, pool));
657
658 /* Now, delete the youngest revprop file, and recover again. This
659 time we want to see an error! */
660 SVN_ERR(svn_io_remove_file2(
661 svn_dirent_join_many(pool, REPO_NAME, PATH_REVS_DIR,
662 apr_psprintf(pool, "%ld/p%ld",
663 after_rev / SHARD_SIZE,
664 after_rev),
665 SVN_VA_NULL),
666 FALSE, pool));
667 err = svn_fs_recover(REPO_NAME, NULL, NULL, pool);
668 if (! err)
669 return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
670 "Expected SVN_ERR_FS_CORRUPT error; got none");
671 if (err->apr_err != SVN_ERR_FS_CORRUPT)
672 return svn_error_create(SVN_ERR_TEST_FAILED, err,
673 "Expected SVN_ERR_FS_CORRUPT error; got:");
674 svn_error_clear(err);
675 return SVN_NO_ERROR;
676 }
677 #undef REPO_NAME
678 #undef MAX_REV
679 #undef SHARD_SIZE
680
681 /* ------------------------------------------------------------------------ */
682 /* Regression test for issue #4320 (fsfs file-hinting fails when reading a rep
683 from the transaction that is commiting rev = SHARD_SIZE). */
684 #define REPO_NAME "test-repo-file-hint-at-shard-boundary"
685 #define SHARD_SIZE 4
686 #define MAX_REV (SHARD_SIZE - 1)
687 static svn_error_t *
file_hint_at_shard_boundary(const svn_test_opts_t * opts,apr_pool_t * pool)688 file_hint_at_shard_boundary(const svn_test_opts_t *opts,
689 apr_pool_t *pool)
690 {
691 apr_pool_t *subpool;
692 svn_fs_t *fs;
693 svn_fs_txn_t *txn;
694 svn_fs_root_t *txn_root;
695 const char *file_contents;
696 svn_stringbuf_t *retrieved_contents;
697 svn_error_t *err = SVN_NO_ERROR;
698
699 /* Create a packed FS and MAX_REV revisions */
700 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool));
701
702 /* Reopen the filesystem */
703 subpool = svn_pool_create(pool);
704 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, subpool, subpool));
705
706 /* Revision = SHARD_SIZE */
707 file_contents = get_rev_contents(SHARD_SIZE, subpool);
708 SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool));
709 SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
710 SVN_ERR(svn_test__set_file_contents(txn_root, "iota", file_contents,
711 subpool));
712
713 /* Retrieve the file. */
714 SVN_ERR(svn_test__get_file_contents(txn_root, "iota", &retrieved_contents,
715 subpool));
716 if (strcmp(retrieved_contents->data, file_contents))
717 {
718 err = svn_error_create(SVN_ERR_TEST_FAILED, err,
719 "Retrieved incorrect contents from iota.");
720 }
721
722 /* Close the repo. */
723 svn_pool_destroy(subpool);
724
725 return err;
726 }
727 #undef REPO_NAME
728 #undef MAX_REV
729 #undef SHARD_SIZE
730
731 /* ------------------------------------------------------------------------ */
732 #define REPO_NAME "test-repo-fsx-info"
733 #define SHARD_SIZE 3
734 #define MAX_REV 5
735 static svn_error_t *
test_info(const svn_test_opts_t * opts,apr_pool_t * pool)736 test_info(const svn_test_opts_t *opts,
737 apr_pool_t *pool)
738 {
739 svn_fs_t *fs;
740 const svn_fs_fsx_info_t *fsx_info;
741 const svn_fs_info_placeholder_t *info;
742
743 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
744 pool));
745
746 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
747 SVN_ERR(svn_fs_info(&info, fs, pool, pool));
748 info = svn_fs_info_dup(info, pool, pool);
749
750 SVN_TEST_STRING_ASSERT(opts->fs_type, info->fs_type);
751
752 /* Bail (with success) on known-untestable scenarios */
753 if (strcmp(opts->fs_type, "fsx") != 0)
754 return SVN_NO_ERROR;
755
756 fsx_info = (const void *)info;
757 SVN_TEST_ASSERT(fsx_info->shard_size == SHARD_SIZE);
758 SVN_TEST_ASSERT(fsx_info->min_unpacked_rev
759 == (MAX_REV + 1) / SHARD_SIZE * SHARD_SIZE);
760
761 return SVN_NO_ERROR;
762 }
763 #undef REPO_NAME
764 #undef SHARD_SIZE
765 #undef MAX_REV
766
767 /* ------------------------------------------------------------------------ */
768 #define REPO_NAME "test-repo-fsx-rev-container"
769 #define SHARD_SIZE 3
770 #define MAX_REV 5
771 static svn_error_t *
test_reps(const svn_test_opts_t * opts,apr_pool_t * pool)772 test_reps(const svn_test_opts_t *opts,
773 apr_pool_t *pool)
774 {
775 svn_fs_t *fs = NULL;
776 svn_fs_x__reps_builder_t *builder;
777 svn_fs_x__reps_t *container;
778 svn_stringbuf_t *serialized;
779 svn_stream_t *stream;
780 svn_stringbuf_t *contents = svn_stringbuf_create_ensure(10000, pool);
781 int i;
782
783 for (i = 0; i < 10000; ++i)
784 {
785 int v, s = 0;
786 for (v = i; v > 0; v /= 10)
787 s += v % 10;
788
789 svn_stringbuf_appendbyte(contents, (char)(s + ' '));
790 }
791
792 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
793 pool));
794
795 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
796
797 builder = svn_fs_x__reps_builder_create(fs, pool);
798 for (i = 10000; i > 10; --i)
799 {
800 apr_size_t idx;
801 svn_string_t string;
802 string.data = contents->data;
803 string.len = i;
804
805 SVN_ERR(svn_fs_x__reps_add(&idx, builder, &string));
806 }
807
808 serialized = svn_stringbuf_create_empty(pool);
809 stream = svn_stream_from_stringbuf(serialized, pool);
810 SVN_ERR(svn_fs_x__write_reps_container(stream, builder, pool));
811
812 SVN_ERR(svn_stream_reset(stream));
813 SVN_ERR(svn_fs_x__read_reps_container(&container, stream, pool, pool));
814 SVN_ERR(svn_stream_close(stream));
815
816 return SVN_NO_ERROR;
817 }
818
819 #undef REPO_NAME
820 #undef SHARD_SIZE
821 #undef MAX_REV
822
823 /* ------------------------------------------------------------------------ */
824 #define REPO_NAME "test-repo-fsx-pack-shard-size-one"
825 #define SHARD_SIZE 1
826 #define MAX_REV 4
827 static svn_error_t *
pack_shard_size_one(const svn_test_opts_t * opts,apr_pool_t * pool)828 pack_shard_size_one(const svn_test_opts_t *opts,
829 apr_pool_t *pool)
830 {
831 svn_string_t *propval;
832 svn_fs_t *fs;
833
834 SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
835 pool));
836 SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
837 /* whitebox: revprop packing special-cases r0, which causes
838 (start_rev==1, end_rev==0) in pack_revprops_shard(). So test that. */
839 SVN_ERR(svn_fs_revision_prop(&propval, fs, 1, SVN_PROP_REVISION_LOG, pool));
840 SVN_TEST_STRING_ASSERT(propval->data, R1_LOG_MSG);
841
842 return SVN_NO_ERROR;
843 }
844 #undef REPO_NAME
845 #undef SHARD_SIZE
846 #undef MAX_REV
847 /* ------------------------------------------------------------------------ */
848 #define REPO_NAME "test-repo-fsx-batch-fsync"
849 static svn_error_t *
test_batch_fsync(const svn_test_opts_t * opts,apr_pool_t * pool)850 test_batch_fsync(const svn_test_opts_t *opts,
851 apr_pool_t *pool)
852 {
853 const char *abspath;
854 svn_fs_x__batch_fsync_t *batch;
855 int i;
856
857 /* Disable this test for non FSX backends because it has no relevance to
858 * them. */
859 if (strcmp(opts->fs_type, "fsx") != 0)
860 return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
861 "this will test FSX repositories only");
862
863 /* Create an empty working directory and let it be cleaned up by the test
864 * harness. */
865 SVN_ERR(svn_dirent_get_absolute(&abspath, REPO_NAME, pool));
866
867 SVN_ERR(svn_io_remove_dir2(abspath, TRUE, NULL, NULL, pool));
868 SVN_ERR(svn_io_make_dir_recursively(abspath, pool));
869 svn_test_add_dir_cleanup(abspath);
870
871 /* Initialize infrastructure with a pool that lives as long as this
872 * application. */
873 SVN_ERR(svn_fs_x__batch_fsync_init(pool));
874
875 /* We use and re-use the same batch object throughout this test. */
876 SVN_ERR(svn_fs_x__batch_fsync_create(&batch, TRUE, pool));
877
878 /* The working directory is new. */
879 SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, abspath, pool));
880
881 /* 1st run: Has to fire up worker threads etc. */
882 for (i = 0; i < 10; ++i)
883 {
884 apr_file_t *file;
885 const char *path = svn_dirent_join(abspath,
886 apr_psprintf(pool, "file%i", i),
887 pool);
888 apr_size_t len = strlen(path);
889
890 SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, path, pool));
891
892 SVN_ERR(svn_io_file_write(file, path, &len, pool));
893 }
894
895 SVN_ERR(svn_fs_x__batch_fsync_run(batch, pool));
896
897 /* 2nd run: Running a batch must leave the container in an empty,
898 * re-usable state. Hence, try to re-use it. */
899 for (i = 0; i < 10; ++i)
900 {
901 apr_file_t *file;
902 const char *path = svn_dirent_join(abspath,
903 apr_psprintf(pool, "new%i", i),
904 pool);
905 apr_size_t len = strlen(path);
906
907 SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, path, pool));
908
909 SVN_ERR(svn_io_file_write(file, path, &len, pool));
910 }
911
912 SVN_ERR(svn_fs_x__batch_fsync_run(batch, pool));
913
914 /* 3rd run: Schedule but don't execute. POOL cleanup shall not fail. */
915 for (i = 0; i < 10; ++i)
916 {
917 apr_file_t *file;
918 const char *path = svn_dirent_join(abspath,
919 apr_psprintf(pool, "another%i", i),
920 pool);
921 apr_size_t len = strlen(path);
922
923 SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, path, pool));
924
925 SVN_ERR(svn_io_file_write(file, path, &len, pool));
926 }
927
928 return SVN_NO_ERROR;
929 }
930 #undef REPO_NAME
931 /* ------------------------------------------------------------------------ */
932
933 /* The test table. */
934
935 static int max_threads = 4;
936
937 static struct svn_test_descriptor_t test_funcs[] =
938 {
939 SVN_TEST_NULL,
940 SVN_TEST_OPTS_PASS(pack_filesystem,
941 "pack a FSX filesystem"),
942 SVN_TEST_OPTS_PASS(pack_even_filesystem,
943 "pack FSX where revs % shard = 0"),
944 SVN_TEST_OPTS_PASS(read_packed_fs,
945 "read from a packed FSX filesystem"),
946 SVN_TEST_OPTS_PASS(commit_packed_fs,
947 "commit to a packed FSX filesystem"),
948 SVN_TEST_OPTS_PASS(get_set_revprop_packed_fs,
949 "get/set revprop while packing FSX filesystem"),
950 SVN_TEST_OPTS_PASS(get_set_large_revprop_packed_fs,
951 "get/set large packed revprops in FSX"),
952 SVN_TEST_OPTS_PASS(get_set_huge_revprop_packed_fs,
953 "get/set huge packed revprops in FSX"),
954 SVN_TEST_OPTS_PASS(recover_fully_packed,
955 "recover a fully packed filesystem"),
956 SVN_TEST_OPTS_PASS(file_hint_at_shard_boundary,
957 "test file hint at shard boundary"),
958 SVN_TEST_OPTS_PASS(test_info,
959 "test svn_fs_info"),
960 SVN_TEST_OPTS_PASS(test_reps,
961 "test representations container"),
962 SVN_TEST_OPTS_PASS(pack_shard_size_one,
963 "test packing with shard size = 1"),
964 SVN_TEST_OPTS_PASS(test_batch_fsync,
965 "test batch fsync"),
966 SVN_TEST_NULL
967 };
968
969 SVN_TEST_MAIN
970